[Checkins] SVN: z3c.blobfile/trunk/ Added an evolution script and functional doctests for replacing zope.app.file files with blobfiles.

Uwe Oestermeier u.oestermeier at iwm-kmrc.de
Wed Nov 14 07:07:51 EST 2007


Log message for revision 81827:
  Added an evolution script and functional doctests for replacing zope.app.file files with blobfiles.

Changed:
  U   z3c.blobfile/trunk/buildout.cfg
  U   z3c.blobfile/trunk/src/z3c/blobfile/blobfile.txt
  U   z3c.blobfile/trunk/src/z3c/blobfile/configure.zcml
  U   z3c.blobfile/trunk/src/z3c/blobfile/file.py
  U   z3c.blobfile/trunk/src/z3c/blobfile/ftesting.zcml
  A   z3c.blobfile/trunk/src/z3c/blobfile/ftests.py
  A   z3c.blobfile/trunk/src/z3c/blobfile/generations/
  A   z3c.blobfile/trunk/src/z3c/blobfile/generations/__init__.py
  A   z3c.blobfile/trunk/src/z3c/blobfile/generations/evolve1.py
  A   z3c.blobfile/trunk/src/z3c/blobfile/generations/install.py
  U   z3c.blobfile/trunk/src/z3c/blobfile/image.py
  U   z3c.blobfile/trunk/src/z3c/blobfile/interfaces.py
  A   z3c.blobfile/trunk/src/z3c/blobfile/overrides.zcml
  U   z3c.blobfile/trunk/src/z3c/blobfile/testing.py
  U   z3c.blobfile/trunk/src/z3c/blobfile/tests.py

-=-
Modified: z3c.blobfile/trunk/buildout.cfg
===================================================================
--- z3c.blobfile/trunk/buildout.cfg	2007-11-14 11:26:48 UTC (rev 81826)
+++ z3c.blobfile/trunk/buildout.cfg	2007-11-14 12:07:51 UTC (rev 81827)
@@ -6,3 +6,4 @@
 [test]
 recipe = zc.recipe.testrunner
 eggs = z3c.blobfile [test]
+defaults = ['--tests-pattern', '^f?tests$', '-v']
\ No newline at end of file

Modified: z3c.blobfile/trunk/src/z3c/blobfile/blobfile.txt
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/blobfile.txt	2007-11-14 11:26:48 UTC (rev 81826)
+++ z3c.blobfile/trunk/src/z3c/blobfile/blobfile.txt	2007-11-14 12:07:51 UTC (rev 81827)
@@ -1,15 +1,69 @@
 Blob File Implementation
 ========================
 
+This implementation of zope.app.file.interfaces.IFile takes advantage
+of the new ZODB blob support and tries to be completely backward compatible to
+the existing file implementation in zope.app.file.
 
+Let's assume that you have already an existing database with zope.app.file
+content types:
 
+    >>> root = getRootFolder()
+    >>> from zope.app.file import File, Image
+    
+    >>> from z3c.blobfile import testing
+    >>> root[u'file'] = File('A text file', contentType='text/plain')
+    >>> root[u'image'] = Image(testing.zptlogo)
+    
+Note that we cannot assume that these objects exist in isolation. Many of
+them probably are annotated, indexed, some even may be registered as utility
+etc.
 
+Therefore we need a very conservative evolution strategy. We simply replace
+the __class__ attribute and the data section. We will not test all relations
+to all other objects, since this is largely application dependent. Here
+we only take the ZopeDublinCore timestamps as an example that our evolution
+step leaves as many things untouched as possible. 
+
+    >>> from zope.dublincore.interfaces import IZopeDublinCore
+    >>> import datetime
+    
+    >>> IZopeDublinCore(root[u'file']).created = datetime.datetime.utcnow()
+    >>> t1 = IZopeDublinCore(root[u'file']).created 
+    >>> IZopeDublinCore(root[u'file']).title = u'No evolution'
+
+Now we perform the basic evolution steps:
+    
+    >>> from z3c.blobfile.generations.evolve1 import evolveZopeAppFile
+    >>> evolveZopeAppFile(root)
+  
+The file data remain the same ...
+
+    >>> root[u'file'].data
+    'A text file'
+    >>> root[u'file'].contentType
+    'text/plain'
+    
+    >>> root[u'image'].data == testing.zptlogo
+    True
+    
+and so are the annotations:
+
+    >>> IZopeDublinCore(root[u'file']).created == t1
+    True
+    >>> IZopeDublinCore(root[u'file']).title
+    u'No evolution'
+    
+IMPORTANT: If your application needs to be aware of the hidden type change, 
+you must provide your own evolution procedure.
+
+
 Compatibility with zope.app.file.File
 -------------------------------------
 
-This test mimics exactly the tests of the zope.app.file package:
+The following tests mimic exactly the tests of the zope.app.file package.
 
-    Let's test the constructor:
+Let's test the constructor:
 
     >>> file = File()
     >>> file.contentType
@@ -36,7 +90,7 @@
     'Foobar'
 
 
-    Let's test the mutators:
+Let's test the mutators:
 
     >>> file = File()
     >>> file.contentType = 'text/plain'
@@ -53,11 +107,11 @@
     TypeError: Cannot set None data on a file.
 
 
-    Let's test large data input:
+Let's test large data input:
 
     >>> file = File()
 
-    Insert as string:
+Insert as string:
 
     >>> file.data = 'Foobar'*60000
     >>> file.getSize()
@@ -65,8 +119,9 @@
     >>> file.data == 'Foobar'*60000
     True
 
-    Insert data as FileChunk:
+Insert data as FileChunk:
 
+    >>> from zope.app.file.file import FileChunk
     >>> fc = FileChunk('Foobar'*4000)
     >>> file.data = fc
     >>> file.getSize()
@@ -74,7 +129,7 @@
     >>> file.data == 'Foobar'*4000
     True
 
-    Insert data from file object:
+Insert data from file object:
 
     >>> import cStringIO
     >>> sio = cStringIO.StringIO()
@@ -87,7 +142,7 @@
     True
 
 
-    Last, but not least, verify the interface:
+Last, but not least, verify the interface:
 
     >>> from zope.interface.verify import verifyClass
     >>> zope.app.file.interfaces.IFile.implementedBy(File)
@@ -99,6 +154,7 @@
 Test of Filerepresentation Adapters
 -----------------------------------
 
+    >>> from zope.app.file.file import FileReadFile
     >>> file = File()
     >>> content = "This is some file\\ncontent."
     >>> file.data = content
@@ -108,7 +164,7 @@
     >>> FileReadFile(file).size() == len(content)
     True
 
-
+    >>> from zope.app.file.file import FileWriteFile
     >>> file = File()
     >>> content = "This is some file\\ncontent."
     >>> FileWriteFile(file).write(content)

Modified: z3c.blobfile/trunk/src/z3c/blobfile/configure.zcml
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/configure.zcml	2007-11-14 11:26:48 UTC (rev 81826)
+++ z3c.blobfile/trunk/src/z3c/blobfile/configure.zcml	2007-11-14 12:07:51 UTC (rev 81827)
@@ -10,6 +10,40 @@
              in the add menu or not  
   -->
 
+  <class class=".file.File">
+
+    <require
+        permission="zope.View"
+        interface=".interfaces.IBlobFile"
+        />
+
+    <require
+        permission="zope.ManageContent"
+        set_schema=".interfaces.IBlobFile"
+        />
+
+    <implements
+       interface="zope.annotation.interfaces.IAttributeAnnotatable"
+       />
+  </class>
+
+  <class class=".image.Image">
+
+    <require
+        permission="zope.View"
+        interface=".interfaces.IBlobImage"
+        />
+
+    <require
+        permission="zope.ManageContent"
+        set_schema="zope.app.file.interfaces.IFile"
+        />
+
+    <implements
+        interface="zope.annotation.interfaces.IAttributeAnnotatable"
+        />
+  </class>
+
   <!-- `IStorage` utilities should be named with the dotted name
        referencing the implementation. 
   -->

Modified: z3c.blobfile/trunk/src/z3c/blobfile/file.py
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/file.py	2007-11-14 11:26:48 UTC (rev 81826)
+++ z3c.blobfile/trunk/src/z3c/blobfile/file.py	2007-11-14 12:07:51 UTC (rev 81827)
@@ -11,12 +11,7 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""File content component
-
-TODO: 
-- we should rename `_data` to `_blob` in `File`
-- should we then keep `_data` in `File` for backwards compatibility?
-"""
+"""File content component"""
 __docformat__ = 'restructuredtext'
 
 from persistent import Persistent
@@ -42,12 +37,12 @@
     size = 0
     
     def __init__(self, data='', contentType=''):
-        self._data = Blob()
+        self._blob = Blob()
         self.contentType = contentType
         self._setData(data)
 
     def open(self, mode="r"):
-        return self._data.open(mode)
+        return self._blob.open(mode)
 
     def _setData(self, data):
         # Search for a storable that is able to store the data
@@ -55,21 +50,22 @@
                                data.__class__.__name__))
         storable = zope.component.getUtility(interfaces.IStorage, 
                                              name=dottedName)
-        storable.store(data, self._data)
+        storable.store(data, self._blob)
 
     def _getData(self):
-        fp = self._data.open('r')
+        fp = self._blob.open('r')
         data = fp.read()
         fp.close()
         return data
-
+        
+    _data = property(_getData, _setData)   
     data = property(_getData, _setData)    
 
     @property
     def size(self):
-        if self._data == "":
+        if self._blob == "":
             return 0
-        reader = self._data.open()
+        reader = self._blob.open()
         reader.seek(0,2)
         size = int(reader.tell())
         reader.close()

Modified: z3c.blobfile/trunk/src/z3c/blobfile/ftesting.zcml
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/ftesting.zcml	2007-11-14 11:26:48 UTC (rev 81826)
+++ z3c.blobfile/trunk/src/z3c/blobfile/ftesting.zcml	2007-11-14 12:07:51 UTC (rev 81827)
@@ -13,7 +13,10 @@
   <include package="zope.app.authentication" />
   <include package="zope.app.securitypolicy" />
   <include package="zope.app.file"/>
-
+  <include package="zope.annotation"/>
+  <include package="zope.dublincore"/>
+  <include package="z3c.blobfile"/>
+  
   <securityPolicy
       component="zope.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
 

Added: z3c.blobfile/trunk/src/z3c/blobfile/ftests.py
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/ftests.py	                        (rev 0)
+++ z3c.blobfile/trunk/src/z3c/blobfile/ftests.py	2007-11-14 12:07:51 UTC (rev 81827)
@@ -0,0 +1,33 @@
+import unittest
+
+import zope, transaction
+import os.path
+from zope.testing import doctest, doctestunit, module
+
+import testing
+
+def setUp(test):
+    module.setUp(test, 'z3c.blobfile.readme_txt')
+
+def tearDown(test):
+    module.tearDown(test, 'z3c.blobfile.readme_txt')
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    
+    globs = {'zope': zope,
+            'transaction': transaction,
+            'pprint': doctestunit.pprint}
+
+    test = testing.FunctionalBlobDocFileSuite('blobfile.txt',
+                package='z3c.blobfile',
+                globs=globs,
+                optionflags=doctest.NORMALIZE_WHITESPACE+doctest.ELLIPSIS)
+    test.layer = testing.BlobFileLayer
+    
+    suite.addTests((test,))
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: z3c.blobfile/trunk/src/z3c/blobfile/generations/__init__.py
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/generations/__init__.py	                        (rev 0)
+++ z3c.blobfile/trunk/src/z3c/blobfile/generations/__init__.py	2007-11-14 12:07:51 UTC (rev 81827)
@@ -0,0 +1,10 @@
+
+from zope.app.generations.generations import SchemaManager
+
+
+package_name = 'z3c.blobfile.generations'
+
+BebopPersonSchemaManager = SchemaManager(
+     minimum_generation=1,
+     generation=2,
+     package_name=package_name)

Added: z3c.blobfile/trunk/src/z3c/blobfile/generations/evolve1.py
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/generations/evolve1.py	                        (rev 0)
+++ z3c.blobfile/trunk/src/z3c/blobfile/generations/evolve1.py	2007-11-14 12:07:51 UTC (rev 81827)
@@ -0,0 +1,43 @@
+
+import zope.interface
+import zope.component
+
+from zope.app.generations.utility import findObjectsProviding
+from zope.app.zopeappgenerations import getRootFolder
+
+from zope.app.file.interfaces import IFile
+import zope.app.file
+from ZODB.blob import Blob
+import z3c.blobfile.file
+import z3c.blobfile.image
+
+
+def evolveZopeAppFile(root):
+    """Replaces the classes and data of zope.app.file objects.
+    
+    Leaves annotations, key references, int id etc. intact.
+    Doesn't throw an ObjectModify event.
+    """
+    for file in findObjectsProviding(root, IFile):
+        data = file.data
+        
+        file._blob = Blob()
+        
+        if isinstance(file, zope.app.file.File):
+            file.__class__ = z3c.blobfile.file.File
+           
+        if isinstance(file, zope.app.file.Image):
+            file.__class__ = z3c.blobfile.image.Image    
+            
+        file.data = data    
+         
+    
+def evolve(context):
+    """
+    Replaces all zope.app.file content objects with z3c.blobfile counterparts.
+    """
+
+    root = getRootFolder(context)
+
+    evolveCatalog(root)
+    evolvePerson(root)

Added: z3c.blobfile/trunk/src/z3c/blobfile/generations/install.py
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/generations/install.py	                        (rev 0)
+++ z3c.blobfile/trunk/src/z3c/blobfile/generations/install.py	2007-11-14 12:07:51 UTC (rev 81827)
@@ -0,0 +1,19 @@
+
+from z3c.blobfile.generations import package_name
+
+
+def evolve(context):
+    """run all evolutions beginning with generation 1"""
+
+    print "SchemaManager(%s): install" % package_name
+    generation = 1
+    while True:
+        name = "%s.evolve%d" % (package_name, generation)
+        try:
+            evolver = __import__(name, {}, {}, ['*'])
+        except ImportError:
+            break
+        else:
+            print "SchemaManager(%s): evolve%s" % (package_name, generation)
+            evolver.evolve(context)
+            generation += 1

Modified: z3c.blobfile/trunk/src/z3c/blobfile/image.py
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/image.py	2007-11-14 11:26:48 UTC (rev 81826)
+++ z3c.blobfile/trunk/src/z3c/blobfile/image.py	2007-11-14 12:07:51 UTC (rev 81827)
@@ -11,10 +11,7 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Image content type implementation
-
-$Id: image.py 76693 2007-06-14 13:39:36Z mgedmin $
-"""
+"""Image content type implementation"""
 __docformat__ = 'restructuredtext'
 
 import struct
@@ -31,29 +28,42 @@
 
 from ZODB.blob import Blob
 
+IMAGE_INFO_BYTES = 1024
+
 class Image(File):
     implements(IImage)
 
     def __init__(self, data=''):
         '''See interface `IFile`'''
-        self._data = Blob()
-        self.contentType, self._width, self._height = getImageInfo(data)
+        self._blob = Blob()
         self.data = data
+        firstbytes = self.getFirstBytes()
+        self.contentType, self._width, self._height = getImageInfo(firstbytes)
 
     def _setData(self, data):
-        fp = self._data.open('w')
-        fp.write(data)
-        fp.close()
-        contentType, self._width, self._height = getImageInfo(data)
+        super(Image, self)._setData(data)
+        firstbytes = self.getFirstBytes()
+        contentType, self._width, self._height = getImageInfo(firstbytes)
         if contentType:
             self.contentType = contentType
 
+    def getFirstBytes(self):
+        """Returns the first bytes of the file.
+        
+        Returns an amount which is sufficient to determine the image type.
+        """
+        fp = self.open('r')
+        firstbytes = fp.read(IMAGE_INFO_BYTES)
+        fp.close()
+        return firstbytes
+    
     def getImageSize(self):
-        '''See interface `IImage`'''
+        """See interface `IImage`"""
         return (self._width, self._height)
 
     data = property(File._getData, _setData)
 
+
 class ImageSized(object):
     implements(ISized)
 
@@ -81,6 +91,7 @@
         # i18nextract and never show up in message catalogs
         return _(byte_size + ' ${width}x${height}', mapping=mapping)
 
+
 class FileFactory(object):
 
     def __init__(self, context):
@@ -91,12 +102,11 @@
             content_type, width, height = getImageInfo(data)
         if not content_type:
             content_type, encoding = guess_content_type(name, data, '')
-
         if content_type.startswith('image/'):
             return Image(data)
-
         return File(data, content_type)
 
+
 def getImageInfo(data):
     data = str(data)
     size = len(data)

Modified: z3c.blobfile/trunk/src/z3c/blobfile/interfaces.py
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/interfaces.py	2007-11-14 11:26:48 UTC (rev 81826)
+++ z3c.blobfile/trunk/src/z3c/blobfile/interfaces.py	2007-11-14 12:07:51 UTC (rev 81827)
@@ -21,7 +21,22 @@
 
 import zope.app.file.interfaces
 
+class IOpenable(zope.interface.Interface):
+    """Openable file
+    """
 
+    def open(mode):
+        """Open file and return the file descriptor
+        """
+
+class IBlobFile(zope.app.file.interfaces.IFile, IOpenable):
+    """A file that uses Blobs as data storage."""
+
+
+class IBlobImage(zope.app.file.interfaces.IImage, IOpenable):
+    """An image that uses Blobs as data storage."""
+
+    
 class IStorage(zope.interface.Interface):
     """Store file data
     """
@@ -29,19 +44,9 @@
     def store(data, blob):
         """Store the data into the blob
 
-	Raises NonStorable if data is not storable.
+	    Raises NonStorable if data is not storable.
         """
 
-
-class IOpenable(zope.interface.Interface):
-    """Openable file
-    """
-
-    def open(mode):
-        """Open file and return the file descriptor
-        """
-
-
 class NotStorable(Exception):
     """Data is not storable
     """

Added: z3c.blobfile/trunk/src/z3c/blobfile/overrides.zcml
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/overrides.zcml	                        (rev 0)
+++ z3c.blobfile/trunk/src/z3c/blobfile/overrides.zcml	2007-11-14 12:07:51 UTC (rev 81827)
@@ -0,0 +1,28 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:browser="http://namespaces.zope.org/browser">
+
+  <!-- Provide local overrides of standard configurations -->
+
+    <!-- content classes -->
+
+  <class class=".file.File">
+
+    <factory
+        id="zope.app.content.File"
+        title="File"
+        description="A File"
+        />
+
+  </class>
+
+  <class class=".image.Image">
+
+    <factory
+        id="zope.app.content.Image"
+        title="Image"
+        description="An Image"
+        />
+
+  </class>
+
+</configure>

Modified: z3c.blobfile/trunk/src/z3c/blobfile/testing.py
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/testing.py	2007-11-14 11:26:48 UTC (rev 81826)
+++ z3c.blobfile/trunk/src/z3c/blobfile/testing.py	2007-11-14 12:07:51 UTC (rev 81827)
@@ -18,9 +18,117 @@
 
 __docformat__ = "reStructuredText"
 
-import os
-from zope.app.testing.functional import ZCMLLayer
+import os.path
+import shutil
+import tempfile
 
-AppFileLayer = ZCMLLayer(
+import transaction
+from ZODB.DB import DB
+from ZODB.DemoStorage import DemoStorage
+from ZODB.blob import BlobStorage
+from zope.testing import doctest
+import zope.app.testing.functional
+from zope.app.component.hooks import setSite
+
+here = os.path.dirname(os.path.realpath(__file__))
+
+zptlogo = (
+    'GIF89a\x10\x00\x10\x00\xd5\x00\x00\xff\xff\xff\xff\xff\xfe\xfc\xfd\xfd'
+    '\xfa\xfb\xfc\xf7\xf9\xfa\xf5\xf8\xf9\xf3\xf6\xf8\xf2\xf5\xf7\xf0\xf4\xf6'
+    '\xeb\xf1\xf3\xe5\xed\xef\xde\xe8\xeb\xdc\xe6\xea\xd9\xe4\xe8\xd7\xe2\xe6'
+    '\xd2\xdf\xe3\xd0\xdd\xe3\xcd\xdc\xe1\xcb\xda\xdf\xc9\xd9\xdf\xc8\xd8\xdd'
+    '\xc6\xd7\xdc\xc4\xd6\xdc\xc3\xd4\xda\xc2\xd3\xd9\xc1\xd3\xd9\xc0\xd2\xd9'
+    '\xbd\xd1\xd8\xbd\xd0\xd7\xbc\xcf\xd7\xbb\xcf\xd6\xbb\xce\xd5\xb9\xcd\xd4'
+    '\xb6\xcc\xd4\xb6\xcb\xd3\xb5\xcb\xd2\xb4\xca\xd1\xb2\xc8\xd0\xb1\xc7\xd0'
+    '\xb0\xc7\xcf\xaf\xc6\xce\xae\xc4\xce\xad\xc4\xcd\xab\xc3\xcc\xa9\xc2\xcb'
+    '\xa8\xc1\xca\xa6\xc0\xc9\xa4\xbe\xc8\xa2\xbd\xc7\xa0\xbb\xc5\x9e\xba\xc4'
+    '\x9b\xbf\xcc\x98\xb6\xc1\x8d\xae\xbaFgs\x00\x00\x00\x00\x00\x00\x00\x00'
+    '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+    '\x00,\x00\x00\x00\x00\x10\x00\x10\x00\x00\x06z@\x80pH,\x12k\xc8$\xd2f\x04'
+    '\xd4\x84\x01\x01\xe1\xf0d\x16\x9f\x80A\x01\x91\xc0ZmL\xb0\xcd\x00V\xd4'
+    '\xc4a\x87z\xed\xb0-\x1a\xb3\xb8\x95\xbdf8\x1e\x11\xca,MoC$\x15\x18{'
+    '\x006}m\x13\x16\x1a\x1f\x83\x85}6\x17\x1b $\x83\x00\x86\x19\x1d!%)\x8c'
+    '\x866#\'+.\x8ca`\x1c`(,/1\x94B5\x19\x1e"&*-024\xacNq\xba\xbb\xb8h\xbeb'
+    '\x00A\x00;'
+    )
+
+class FunctionalBlobTestSetup(zope.app.testing.functional.FunctionalTestSetup):
+
+    temp_dir_name = None
+
+    def setUp(self):
+        """Prepares for a functional test case."""
+        # Tear down the old demo storage (if any) and create a fresh one
+        transaction.abort()
+        self.db.close()
+        storage = DemoStorage("Demo Storage", self.base_storage)
+        # make a dir
+        temp_dir_name = self.temp_dir_name = tempfile.mkdtemp()
+        # wrap storage with BlobStorage
+        storage = BlobStorage(temp_dir_name, storage)
+        self.db = self.app.db = DB(storage)
+        self.connection = None
+
+    def tearDown(self):
+        """Cleans up after a functional test case."""
+        transaction.abort()
+        if self.connection:
+            self.connection.close()
+            self.connection = None
+        self.db.close()
+        # del dir named '__blob_test__%s' % self.name
+        if self.temp_dir_name is not None:
+            shutil.rmtree(self.temp_dir_name, True)
+            self.temp_dir_name = None
+        setSite(None)
+
+
+class ZCMLLayer(zope.app.testing.functional.ZCMLLayer):
+
+    def setUp(self):
+        self.setup = FunctionalBlobTestSetup(self.config_file)
+
+
+class ZCMLLayer(zope.app.testing.functional.ZCMLLayer):
+
+    def setUp(self):
+        self.setup = FunctionalBlobTestSetup(self.config_file)
+
+def FunctionalBlobDocFileSuite(*paths, **kw):
+    globs = kw.setdefault('globs', {})
+    globs['http'] = zope.app.testing.functional.HTTPCaller()
+    globs['getRootFolder'] = zope.app.testing.functional.getRootFolder
+    globs['sync'] = zope.app.testing.functional.sync
+
+    kw['package'] = doctest._normalize_module(kw.get('package'))
+
+    kwsetUp = kw.get('setUp')
+    def setUp(test):
+        FunctionalBlobTestSetup().setUp()
+
+        if kwsetUp is not None:
+            kwsetUp(test)
+    kw['setUp'] = setUp
+
+    kwtearDown = kw.get('tearDown')
+    def tearDown(test):
+        if kwtearDown is not None:
+            kwtearDown(test)
+        FunctionalBlobTestSetup().tearDown()
+    kw['tearDown'] = tearDown
+
+    if 'optionflags' not in kw:
+        old = doctest.set_unittest_reportflags(0)
+        doctest.set_unittest_reportflags(old)
+        kw['optionflags'] = (old
+                             | doctest.ELLIPSIS
+                             | doctest.REPORT_NDIFF
+                             | doctest.NORMALIZE_WHITESPACE)
+
+    suite = doctest.DocFileSuite(*paths, **kw)
+    suite.layer = zope.app.testing.functional.Functional
+    return suite
+    
+BlobFileLayer = ZCMLLayer(
     os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'),
-    __name__, 'AppFileLayer', allow_teardown=True)
+    __name__, 'BlobFileLayer', allow_teardown=True)

Modified: z3c.blobfile/trunk/src/z3c/blobfile/tests.py
===================================================================
--- z3c.blobfile/trunk/src/z3c/blobfile/tests.py	2007-11-14 11:26:48 UTC (rev 81826)
+++ z3c.blobfile/trunk/src/z3c/blobfile/tests.py	2007-11-14 12:07:51 UTC (rev 81827)
@@ -11,44 +11,20 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Test Image content component
-
-$Id: test_image.py 76693 2007-06-14 13:39:36Z mgedmin $
-"""
+"""Test Image content component"""
 import unittest
+import zope.component
+
 from zope.interface.verify import verifyClass
 from zope.app.file.interfaces import IImage
-from z3c.blobfile.image import Image, FileFactory, ImageSized
+from z3c.blobfile.image import Image, FileFactory, ImageSized, getImageInfo
 from z3c.blobfile.file import File, FileWriteFile, FileReadFile
 
-zptlogo = (
-    'GIF89a\x10\x00\x10\x00\xd5\x00\x00\xff\xff\xff\xff\xff\xfe\xfc\xfd\xfd'
-    '\xfa\xfb\xfc\xf7\xf9\xfa\xf5\xf8\xf9\xf3\xf6\xf8\xf2\xf5\xf7\xf0\xf4\xf6'
-    '\xeb\xf1\xf3\xe5\xed\xef\xde\xe8\xeb\xdc\xe6\xea\xd9\xe4\xe8\xd7\xe2\xe6'
-    '\xd2\xdf\xe3\xd0\xdd\xe3\xcd\xdc\xe1\xcb\xda\xdf\xc9\xd9\xdf\xc8\xd8\xdd'
-    '\xc6\xd7\xdc\xc4\xd6\xdc\xc3\xd4\xda\xc2\xd3\xd9\xc1\xd3\xd9\xc0\xd2\xd9'
-    '\xbd\xd1\xd8\xbd\xd0\xd7\xbc\xcf\xd7\xbb\xcf\xd6\xbb\xce\xd5\xb9\xcd\xd4'
-    '\xb6\xcc\xd4\xb6\xcb\xd3\xb5\xcb\xd2\xb4\xca\xd1\xb2\xc8\xd0\xb1\xc7\xd0'
-    '\xb0\xc7\xcf\xaf\xc6\xce\xae\xc4\xce\xad\xc4\xcd\xab\xc3\xcc\xa9\xc2\xcb'
-    '\xa8\xc1\xca\xa6\xc0\xc9\xa4\xbe\xc8\xa2\xbd\xc7\xa0\xbb\xc5\x9e\xba\xc4'
-    '\x9b\xbf\xcc\x98\xb6\xc1\x8d\xae\xbaFgs\x00\x00\x00\x00\x00\x00\x00\x00'
-    '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
-    '\x00,\x00\x00\x00\x00\x10\x00\x10\x00\x00\x06z@\x80pH,\x12k\xc8$\xd2f\x04'
-    '\xd4\x84\x01\x01\xe1\xf0d\x16\x9f\x80A\x01\x91\xc0ZmL\xb0\xcd\x00V\xd4'
-    '\xc4a\x87z\xed\xb0-\x1a\xb3\xb8\x95\xbdf8\x1e\x11\xca,MoC$\x15\x18{'
-    '\x006}m\x13\x16\x1a\x1f\x83\x85}6\x17\x1b $\x83\x00\x86\x19\x1d!%)\x8c'
-    '\x866#\'+.\x8ca`\x1c`(,/1\x94B5\x19\x1e"&*-024\xacNq\xba\xbb\xb8h\xbeb'
-    '\x00A\x00;'
-    )
+import testing
+import storages
+import interfaces
 
-
-
-
-# XXX: Don't know how to set up utilities for tests correctly::
 def registerUtilities():
-     import zope.component
-     import storages
-     import interfaces
      zope.component.provideUtility(storages.StringStorable(),
                                    interfaces.IStorage,
                                    name="__builtin__.str")
@@ -86,8 +62,8 @@
         image.contentType = 'image/jpeg'
         self.assertEqual(image.contentType, 'image/jpeg')
 
-        image._setData(zptlogo)
-        self.assertEqual(image.data, zptlogo)
+        image._setData(testing.zptlogo)
+        self.assertEqual(image.data, testing.zptlogo)
         self.assertEqual(image.contentType, 'image/gif')
         self.assertEqual(image.getImageSize(), (16, 16))
 
@@ -143,7 +119,7 @@
         factory = FileFactory(None)
         f = factory("spam.txt", "image/foo", "hello world")
         self.assert_(isinstance(f, Image), f)
-        f = factory("spam.txt", "", zptlogo)
+        f = factory("spam.txt", "", testing.zptlogo)
         self.assert_(isinstance(f, Image), f)
 
     def test_text(self):
@@ -154,10 +130,10 @@
         f = factory("spam.txt", "", "\0\1\2\3\4")
         self.assert_(isinstance(f, File), f)
         self.assert_(not isinstance(f, Image), f)
-        f = factory("spam.txt", "text/splat", zptlogo)
+        f = factory("spam.txt", "text/splat", testing.zptlogo)
         self.assert_(isinstance(f, File), f)
         self.assert_(not isinstance(f, Image), f)
-        f = factory("spam.txt", "application/splat", zptlogo)
+        f = factory("spam.txt", "application/splat", testing.zptlogo)
         self.assert_(isinstance(f, File), f)
         self.assert_(not isinstance(f, Image), f)
 
@@ -193,21 +169,21 @@
         self.assertEqual(s.sizeForDisplay().mapping['height'], '?')
 
     def test_getImageInfo(self):
-        from zope.app.file.image import getImageInfo
-        t, w, h = getImageInfo("\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00C")
+        t, w, h = getImageInfo('\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01'
+                               '\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00C')
+        self.assertEqual(t, "image/jpeg")
+    
+    def test_getImageInfo_bmp(self):
+        t, w, h = getImageInfo('BMl\x05\x00\x00\x00\x00\x00\x006\x04\x00\x00('
+                               '\x00\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00'
+                               '\x01\x00\x08\x00\x01\x00\x00\x006\x01\x00\x00'
+                               '\x12\x0b\x00\x00\x12\x0b\x00\x00\x00\x01\x00'
+                               '... and so on ...')
+        self.assertEqual(t, "image/x-ms-bmp")
+        self.assertEqual(w, 16)
+        self.assertEqual(h, 16)
 
-#     def test_getImageInfo_bmp(self):
-#         from zope.app.file.image import getImageInfo
-#         t, w, h = getImageInfo('BMl\x05\x00\x00\x00\x00\x00\x006\x04\x00\x00('
-#                                '\x00\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00'
-#                                '\x01\x00\x08\x00\x01\x00\x00\x006\x01\x00\x00'
-#                                '\x12\x0b\x00\x00\x12\x0b\x00\x00\x00\x01\x00'
-#                                '... and so on ...')
-#         self.assertEqual(t, "image/x-ms-bmp")
-#         self.assertEqual(w, 16)
-#         self.assertEqual(h, 16)
 
-
 def test_suite():
     return unittest.TestSuite((
         unittest.makeSuite(TestImage),



More information about the Checkins mailing list