Back to index

plone3  3.1.7
test_Image.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
00004 #
00005 # This software is subject to the provisions of the Zope Public License,
00006 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
00007 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
00008 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00009 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
00010 # FOR A PARTICULAR PURPOSE.
00011 #
00012 ##############################################################################
00013 """ Unit tests for Image module.
00014 
00015 $Id: test_Image.py 77186 2007-06-28 19:06:19Z yuppie $
00016 """
00017 
00018 import unittest
00019 from Testing import ZopeTestCase
00020 
00021 from os.path import join as path_join
00022 from cStringIO import StringIO
00023 
00024 import transaction
00025 from AccessControl.SecurityManagement import newSecurityManager
00026 from AccessControl.User import UnrestrictedUser
00027 from zope.app.component.hooks import setSite
00028 from zope.testing.cleanup import cleanUp
00029 
00030 from Products.CMFCore.testing import ConformsToContent
00031 from Products.CMFCore.tests.base.dummy import DummyCachingManager
00032 from Products.CMFCore.tests.base.dummy import DummyCachingManagerWithPolicy
00033 from Products.CMFCore.tests.base.dummy import DummySite
00034 from Products.CMFCore.tests.base.dummy import DummyTool
00035 from Products.CMFCore.tests.base.testcase import RequestTest
00036 from Products.CMFDefault import tests
00037 from Products.CMFDefault.testing import FunctionalLayer
00038 
00039 TESTS_HOME = tests.__path__[0]
00040 TEST_JPG = path_join(TESTS_HOME, 'TestImage.jpg')
00041 
00042 
00043 class TestImageElement(ConformsToContent, unittest.TestCase):
00044 
00045     def _getTargetClass(self):
00046         from Products.CMFDefault.Image import Image
00047 
00048         return Image
00049 
00050     def _makeOne(self, *args, **kw):
00051         return self._getTargetClass()(*args, **kw)
00052 
00053     def setUp(self):
00054         self.site = DummySite('site')
00055         self.site._setObject( 'portal_membership', DummyTool() )
00056 
00057     def test_z3interfaces(self):
00058         from zope.interface.verify import verifyClass
00059         from Products.CMFDefault.interfaces import IFile
00060         from Products.CMFDefault.interfaces import IImage
00061         from Products.CMFDefault.interfaces import IMutableFile
00062         from Products.CMFDefault.interfaces import IMutableImage
00063 
00064         verifyClass(IFile, self._getTargetClass())
00065         verifyClass(IImage, self._getTargetClass())
00066         verifyClass(IMutableFile, self._getTargetClass())
00067         verifyClass(IMutableImage, self._getTargetClass())
00068 
00069     def test_getId_on_old_Image_instance(self):
00070         image = self.site._setObject('testimage', self._makeOne('testimage'))
00071         self.assertEqual(image.getId(), 'testimage')
00072         self.assertEqual(image.id, 'testimage')
00073         # Mimick old instance when base classes had OFS.Image.Image first
00074         image.__name__ = 'testimage'
00075         delattr(image, 'id')
00076         self.assertEqual(image.getId(), 'testimage')
00077         self.assertEqual(image.id(), 'testimage')
00078 
00079     def test_EditWithEmptyFile(self):
00080         # Test handling of empty file uploads
00081         image = self.site._setObject('testimage', self._makeOne('testimage'))
00082 
00083         testfile = open(TEST_JPG, 'rb')
00084         image.edit(file=testfile)
00085         testfile.seek(0,2)
00086         testfilesize = testfile.tell()
00087         testfile.close()
00088 
00089         assert image.get_size() == testfilesize
00090 
00091         emptyfile = StringIO()
00092         image.edit(file=emptyfile)
00093 
00094         assert image.get_size() > 0
00095         assert image.get_size() == testfilesize
00096 
00097     def test_Image_setFormat(self):
00098         # Setting the DC.format must also set the content_type property
00099         image = self._makeOne('testimage', format='image/jpeg')
00100         self.assertEqual(image.Format(), 'image/jpeg')
00101         self.assertEqual(image.content_type, 'image/jpeg')
00102         image.setFormat('image/gif')
00103         self.assertEqual(image.Format(), 'image/gif')
00104         self.assertEqual(image.content_type, 'image/gif')
00105 
00106     def test_ImageContentTypeUponConstruction(self):
00107         # Test the content type after calling the constructor with the
00108         # file object being passed in (http://www.zope.org/Collectors/CMF/370)
00109         testfile = open(TEST_JPG, 'rb')
00110         image = self._makeOne('testimage', file=testfile)
00111         testfile.close()
00112         self.assertEqual(image.Format(), 'image/jpeg')
00113         self.assertEqual(image.content_type, 'image/jpeg')
00114 
00115 
00116 class TestImageCopyPaste(ZopeTestCase.FunctionalTestCase):
00117 
00118     # Tests related to http://www.zope.org/Collectors/CMF/176
00119     # Copy/pasting an image (or file) should reset the object's workflow state.
00120 
00121     layer = FunctionalLayer
00122 
00123     def afterSetUp(self):
00124         setSite(self.app.site)
00125         newSecurityManager(None, UnrestrictedUser('god', '', ['Manager'], ''))
00126 
00127         self.site = self.app.site
00128         self.site.invokeFactory('File', id='file')
00129         self.site.portal_workflow.doActionFor(self.site.file, 'publish')
00130         self.site.invokeFactory('Image', id='image')
00131         self.site.portal_workflow.doActionFor(self.site.image, 'publish')
00132         self.site.invokeFactory('Folder', id='subfolder')
00133         self.subfolder = self.site.subfolder
00134         self.workflow = self.site.portal_workflow
00135         transaction.commit() # Make sure we have _p_jars
00136 
00137     def test_File_CopyPasteResetsWorkflowState(self):
00138         # Copy/pasting a File should reset wf state to private
00139         cb = self.site.manage_copyObjects(['file'])
00140         self.subfolder.manage_pasteObjects(cb)
00141         review_state = self.workflow.getInfoFor(self.subfolder.file, 'review_state')
00142         self.assertEqual(review_state, 'private')
00143 
00144     def test_File_CloneResetsWorkflowState(self):
00145         # Cloning a File should reset wf state to private
00146         self.subfolder.manage_clone(self.site.file, 'file')
00147         review_state = self.workflow.getInfoFor(self.subfolder.file, 'review_state')
00148         self.assertEqual(review_state, 'private')
00149 
00150     def test_File_CutPasteKeepsWorkflowState(self):
00151         # Cut/pasting a File should keep the wf state
00152         cb = self.site.manage_cutObjects(['file'])
00153         self.subfolder.manage_pasteObjects(cb)
00154         review_state = self.workflow.getInfoFor(self.subfolder.file, 'review_state')
00155         self.assertEqual(review_state, 'published')
00156 
00157     def test_File_RenameKeepsWorkflowState(self):
00158         # Renaming a File should keep the wf state
00159         self.site.manage_renameObjects(['file'], ['file2'])
00160         review_state = self.workflow.getInfoFor(self.site.file2, 'review_state')
00161         self.assertEqual(review_state, 'published')
00162 
00163     def test_Image_CopyPasteResetsWorkflowState(self):
00164         #  Copy/pasting an Image should reset wf state to private
00165         cb = self.site.manage_copyObjects(['image'])
00166         self.subfolder.manage_pasteObjects(cb)
00167         review_state = self.workflow.getInfoFor(self.subfolder.image, 'review_state')
00168         self.assertEqual(review_state, 'private')
00169 
00170     def test_Image_CloneResetsWorkflowState(self):
00171         # Cloning an Image should reset wf state to private
00172         self.subfolder.manage_clone(self.site.image, 'image')
00173         review_state = self.workflow.getInfoFor(self.subfolder.image, 'review_state')
00174         self.assertEqual(review_state, 'private')
00175 
00176     def test_Image_CutPasteKeepsWorkflowState(self):
00177         # Cut/pasting an Image should keep the wf state
00178         cb = self.site.manage_cutObjects(['image'])
00179         self.subfolder.manage_pasteObjects(cb)
00180         review_state = self.workflow.getInfoFor(self.subfolder.image, 'review_state')
00181         self.assertEqual(review_state, 'published')
00182 
00183     def test_Image_RenameKeepsWorkflowState(self):
00184         # Renaming an Image should keep the wf state
00185         self.site.manage_renameObjects(['image'], ['image2'])
00186         review_state = self.workflow.getInfoFor(self.site.image2, 'review_state')
00187         self.assertEqual(review_state, 'published')
00188 
00189 
00190 class TestCaching(RequestTest):
00191 
00192     def _extractFile( self ):
00193 
00194         f = open( TEST_JPG, 'rb' )
00195         try:
00196             data = f.read()
00197         finally:
00198             f.close()
00199 
00200         return TEST_JPG, data
00201 
00202     def _getTargetClass(self):
00203         from Products.CMFDefault.Image import Image
00204 
00205         return Image
00206 
00207     def _makeOne(self, *args, **kw):
00208         return self._getTargetClass()(*args, **kw)
00209 
00210     def _setupCachingPolicyManager(self, cpm_object):
00211         self.root.caching_policy_manager = cpm_object
00212 
00213     def tearDown(self):
00214         cleanUp()
00215         RequestTest.tearDown(self)
00216 
00217     def test_index_html_with_304_from_cpm( self ):
00218         self._setupCachingPolicyManager(DummyCachingManagerWithPolicy())
00219         path, ref = self._extractFile()
00220 
00221         from webdav.common import rfc1123_date
00222         from Products.CMFCore.tests.base.dummy import FAKE_ETAG
00223 
00224         self.root.file = self._makeOne('test_file', 'test_image.jpg', file=ref)
00225         file = self.root.file
00226 
00227         mod_time = file.modified()
00228 
00229         self.REQUEST.environ[ 'IF_MODIFIED_SINCE'
00230                             ] = '%s;' % rfc1123_date( mod_time )
00231         self.REQUEST.environ[ 'IF_NONE_MATCH'
00232                             ] = '%s;' % FAKE_ETAG
00233 
00234         data = file.index_html( self.REQUEST, self.RESPONSE )
00235         self.assertEqual( len(data), 0 )
00236         self.assertEqual( self.RESPONSE.getStatus(), 304 )
00237 
00238     def test_caching( self ):
00239         self._setupCachingPolicyManager(DummyCachingManager())
00240         original_len = len(self.RESPONSE.headers)
00241         image = self._makeOne('test_image', 'test_image.jpg')
00242         image = image.__of__(self.root)
00243         image.index_html(self.REQUEST, self.RESPONSE)
00244         headers = self.RESPONSE.headers
00245         self.failUnless(len(headers) >= original_len + 3)
00246         self.failUnless('foo' in headers.keys())
00247         self.failUnless('bar' in headers.keys())
00248         self.assertEqual(headers['test_path'], '/test_image')
00249 
00250     def test_index_html_200_with_cpm( self ):
00251         self._setupCachingPolicyManager(DummyCachingManagerWithPolicy())
00252         path, ref = self._extractFile()
00253 
00254         from webdav.common import rfc1123_date
00255 
00256         file = self._makeOne( 'test_file', 'test_image.jpg', file=ref )
00257         file = file.__of__( self.root )
00258 
00259         mod_time = file.modified()
00260 
00261         data = file.index_html( self.REQUEST, self.RESPONSE )
00262 
00263         # should behave the same as without cpm
00264         self.assertEqual( len( data ), len( ref ) )
00265         self.assertEqual( data, ref )
00266         # ICK!  'HTTPResponse.getHeader' doesn't case-flatten the key!
00267         self.assertEqual( self.RESPONSE.getHeader( 'Content-Length'.lower() )
00268                         , str(len(ref)) )
00269         self.assertEqual( self.RESPONSE.getHeader( 'Content-Type'.lower() )
00270                         , 'image/jpeg' )
00271         self.assertEqual( self.RESPONSE.getHeader( 'Last-Modified'.lower() )
00272                         , rfc1123_date( mod_time ) )
00273 
00274     def test_index_html_with_304_and_caching( self ):
00275 
00276         # See collector #355
00277         self._setupCachingPolicyManager(DummyCachingManager())
00278         original_len = len(self.RESPONSE.headers)
00279         path, ref = self._extractFile()
00280 
00281         from webdav.common import rfc1123_date
00282 
00283         self.root.image = self._makeOne( 'test_image', 'test_image.gif' )
00284         image = self.root.image
00285         transaction.savepoint(optimistic=True)
00286 
00287         mod_time = image.modified()
00288 
00289         self.REQUEST.environ[ 'IF_MODIFIED_SINCE'
00290                             ] = '%s;' % rfc1123_date( mod_time+1 )
00291 
00292         data = image.index_html( self.REQUEST, self.RESPONSE )
00293 
00294         self.assertEqual( data, '' )
00295         self.assertEqual( self.RESPONSE.getStatus(), 304 )
00296 
00297         headers = self.RESPONSE.headers
00298         self.failUnless(len(headers) >= original_len + 3)
00299         self.failUnless('foo' in headers.keys())
00300         self.failUnless('bar' in headers.keys())
00301         self.assertEqual(headers['test_path'], '/test_image')
00302 
00303 
00304 def test_suite():
00305     return unittest.TestSuite((
00306         unittest.makeSuite(TestImageElement),
00307         unittest.makeSuite(TestImageCopyPaste),
00308         unittest.makeSuite(TestCaching),
00309         ))
00310 
00311 if __name__ == '__main__':
00312     from Products.CMFCore.testing import run
00313     run(test_suite())