Back to index

plone3  3.1.7
test_atimage.py
Go to the documentation of this file.
00001 #  ATContentTypes http://plone.org/products/atcontenttypes/
00002 #  Archetypes reimplementation of the CMF core types
00003 #  Copyright (c) 2003-2006 AT Content Types development team
00004 #
00005 #  This program is free software; you can redistribute it and/or modify
00006 #  it under the terms of the GNU General Public License as published by
00007 #  the Free Software Foundation; either version 2 of the License, or
00008 #  (at your option) any later version.
00009 #
00010 #  This program is distributed in the hope that it will be useful,
00011 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 #  GNU General Public License for more details.
00014 #
00015 #  You should have received a copy of the GNU General Public License
00016 #  along with this program; if not, write to the Free Software
00017 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 #
00019 """
00020 """
00021 
00022 __author__ = 'Christian Heimes <tiran@cheimes.de>'
00023 __docformat__ = 'restructuredtext'
00024 
00025 import os, sys
00026 import transaction
00027 if __name__ == '__main__':
00028     execfile(os.path.join(sys.path[0], 'framework.py'))
00029 
00030 from Testing import ZopeTestCase # side effect import. leave it here.
00031 from Products.ATContentTypes.tests import atcttestcase, atctftestcase
00032 
00033 from cStringIO import StringIO
00034 from OFS.Image import Image as OFSImage
00035 
00036 from Products.CMFCore.permissions import View
00037 from Products.CMFCore.permissions import ModifyPortalContent
00038 from Products.Archetypes.interfaces.layer import ILayerContainer
00039 from Products.Archetypes.atapi import *
00040 from Products.ATContentTypes.tests.utils import dcEdit, PACKAGE_HOME
00041 
00042 from Products.ATContentTypes.content.image import ATImage
00043 from Products.ATContentTypes.interfaces import IImageContent
00044 from Products.ATContentTypes.interfaces import IATImage
00045 
00046 from Interface.Verify import verifyObject
00047 
00048 # z3 imports
00049 from Products.ATContentTypes.interface import IATImage as Z3IATImage
00050 from Products.ATContentTypes.interface import IImageContent as Z3IImageContent
00051 from zope.interface.verify import verifyObject as Z3verifyObject
00052 
00053 # third party extension
00054 import exif
00055 
00056 def loadImage(name, size=0):
00057     """Load image from testing directory
00058     """
00059     path = os.path.join(PACKAGE_HOME, 'input', name)
00060     fd = open(path, 'rb')
00061     data = fd.read()
00062     fd.close()
00063     return data
00064 
00065 TEST_EXIF_ERROR = loadImage('canoneye.jpg')
00066 TEST_GIF = loadImage('test.gif')
00067 TEST_GIF_LEN = len(TEST_GIF)
00068 TEST_DIV_ERROR = loadImage('divisionerror.jpg')
00069 TEST_JPEG_FILE = open(os.path.join(PACKAGE_HOME, 'input', 'canoneye.jpg'), 'rb')
00070 # XXX replace it by an image with exif informations but w/o
00071 # the nasty error ...
00072 TEST_JPEG = loadImage('canoneye.jpg')
00073 TEST_JPEG_LEN = len(TEST_JPEG)
00074 
00075 def editATCT(obj):
00076     obj.setImage(TEST_GIF, content_type="image/gif")
00077     dcEdit(obj)
00078 
00079 tests = []
00080 
00081 class TestSiteATImage(atcttestcase.ATCTTypeTestCase):
00082 
00083     klass = ATImage
00084     portal_type = 'Image'
00085     title = 'Image'
00086     meta_type = 'ATImage'
00087     icon = 'image_icon.gif'
00088 
00089     def test_implementsImageContent(self):
00090         iface = IImageContent
00091         self.failUnless(iface.isImplementedBy(self._ATCT))
00092         self.failUnless(verifyObject(iface, self._ATCT))
00093 
00094     def test_z3ImplementsImageContent(self):
00095         iface = Z3IImageContent
00096         self.failUnless(Z3verifyObject(iface, self._ATCT))
00097 
00098     def test_implementsATImage(self):
00099         iface = IATImage
00100         self.failUnless(iface.isImplementedBy(self._ATCT))
00101         self.failUnless(verifyObject(iface, self._ATCT))
00102 
00103     def test_z3ImplementsImageContent(self):
00104         iface = Z3IATImage
00105         self.failUnless(Z3verifyObject(iface, self._ATCT))
00106 
00107     def test_edit(self):
00108         new = self._ATCT
00109         editATCT(new)
00110 
00111     def test_getEXIF(self):
00112         # NOTE: not a real test
00113         exif_data = self._ATCT.getEXIF()
00114         self.failUnless(isinstance(exif_data, dict), type(exif_data))
00115 
00116     def test_exifOrientation(self):
00117         # NOTE: not a real test
00118         r, m = self._ATCT.getEXIFOrientation()
00119 
00120     def test_transform(self):
00121         # NOTE: not a real test
00122         self._ATCT.transformImage(2)
00123 
00124     def test_autotransform(self):
00125         # NOTE: not a real test
00126         self._ATCT.autoTransformImage()
00127 
00128     def test_broken_pil(self):
00129         # PIL has a nasty bug when the image ratio is too extrem like 300x15:
00130         # Module PIL.Image, line 1136, in save
00131         # Module PIL.PngImagePlugin, line 510, in _save
00132         # Module PIL.ImageFile, line 474, in _save
00133         # SystemError: tile cannot extend outside image
00134         atct = self._ATCT
00135 
00136         # test upload
00137         atct.setImage(TEST_GIF, mimetype='image/gif', filename='test.gif')
00138         self.failUnlessEqual(atct.getImage().data, TEST_GIF)
00139 
00140     def test_bobo_hook(self):
00141         atct = self._ATCT
00142         REQUEST = {'method' : 'GET'}
00143         scales = atct.getField('image').getAvailableSizes(atct)
00144         atct.setImage(TEST_GIF, mimetype='image/gif', filename='test.gif')
00145 
00146         img = atct.__bobo_traverse__(REQUEST, 'image')
00147         self.failUnless(isinstance(img, OFSImage), img)
00148 
00149         # test if all scales exist
00150         for scale in scales.keys():
00151             name = 'image_' + scale
00152             img = atct.__bobo_traverse__(REQUEST, name)
00153             self.failUnless(isinstance(img, OFSImage), img)
00154 
00155     def test_division_by_0_pil(self):
00156         # pil generates a division by zero error on some images
00157         atct = self._ATCT
00158 
00159         # test upload
00160         atct.setImage(TEST_DIV_ERROR, mimetype='image/jpeg',
00161                       filename='divisionerror.jpg')
00162         self.failUnlessEqual(atct.getImage().data, TEST_DIV_ERROR)
00163 
00164 
00165     def test_get_size(self):
00166         atct = self._ATCT
00167         editATCT(atct)
00168         self.failUnlessEqual(len(TEST_GIF), TEST_GIF_LEN)
00169         self.failUnlessEqual(atct.get_size(), TEST_GIF_LEN)
00170 
00171     def test_schema_marshall(self):
00172         atct = self._ATCT
00173         schema = atct.Schema()
00174         marshall = schema.getLayerImpl('marshall')
00175         marshallers = [PrimaryFieldMarshaller]
00176         try:
00177             from Products.Marshall import ControlledMarshaller
00178             marshallers.append(ControlledMarshaller)
00179         except ImportError:
00180             pass
00181         self.failUnless(isinstance(marshall, tuple(marshallers)), marshall)
00182 
00183     def test_dcEdit(self):
00184         new = self._ATCT
00185         new.setImage(TEST_GIF, content_type="image/gif")
00186         dcEdit(new)
00187 
00188     def test_broken_exif(self):
00189         # This test fails even with the 2005.05.12 exif version from
00190         #    http://home.cfl.rr.com/genecash/
00191         f = StringIO(TEST_EXIF_ERROR)
00192         exif_data = exif.process_file(f, debug=False)
00193         # probably want to add some tests on returned data. Currently gives
00194         #  ValueError in process_file
00195 
00196     def test_exif_upload(self):
00197         atct = self._ATCT
00198         atct._image_exif = None
00199 
00200         # string upload
00201         atct.setImage(TEST_JPEG)
00202         self.failUnless(len(atct.getEXIF()), atct.getEXIF())
00203         atct._image_exif = None
00204 
00205         # file upload
00206         atct.setImage(TEST_JPEG_FILE)
00207         self.failUnless(len(atct.getEXIF()), atct.getEXIF())
00208         atct._image_exif = None
00209 
00210         # Pdata upload
00211         from OFS.Image import Pdata
00212         pd = Pdata(TEST_JPEG)
00213         atct.setImage(pd)
00214         self.failUnless(len(atct.getEXIF()), atct.getEXIF())
00215         atct._image_exif = None
00216 
00217         # ofs image upload
00218         ofs = atct.getImage()
00219         atct.setImage(ofs)
00220         self.failUnless(len(atct.getEXIF()), atct.getEXIF())
00221         atct._image_exif = None
00222 
00223 tests.append(TestSiteATImage)
00224 
00225 class TestATImageFields(atcttestcase.ATCTFieldTestCase):
00226 
00227     # Title is not a required field, since we don't require them 
00228     # on File/Image - they are taken from the filename if not present.
00229     # "Add the comment 'damn stupid fucking test'" -- optilude ;)
00230     def test_title(self):
00231         pass
00232 
00233     def afterSetUp(self):
00234         atcttestcase.ATCTFieldTestCase.afterSetUp(self)
00235         self._dummy = self.createDummy(klass=ATImage)
00236 
00237     def test_imageField(self):
00238         dummy = self._dummy
00239         field = dummy.getField('image')
00240 
00241         self.failUnless(ILayerContainer.isImplementedBy(field))
00242         self.failUnless(field.required == 1, 'Value is %s' % field.required)
00243         self.failUnless(field.default == '', 'Value is %s' % str(field.default))
00244         self.failUnless(field.searchable == 0, 'Value is %s' % field.searchable)
00245         self.failUnless(field.vocabulary == (),
00246                         'Value is %s' % str(field.vocabulary))
00247         self.failUnless(field.enforceVocabulary == 0,
00248                         'Value is %s' % field.enforceVocabulary)
00249         self.failUnless(field.multiValued == 0,
00250                         'Value is %s' % field.multiValued)
00251         self.failUnless(field.isMetadata == 0, 'Value is %s' % field.isMetadata)
00252         self.failUnless(field.accessor == 'getImage',
00253                         'Value is %s' % field.accessor)
00254         self.failUnless(field.mutator == 'setImage',
00255                         'Value is %s' % field.mutator)
00256         self.failUnless(field.read_permission == View,
00257                         'Value is %s' % field.read_permission)
00258         self.failUnless(field.write_permission == ModifyPortalContent,
00259                         'Value is %s' % field.write_permission)
00260         self.failUnless(field.generateMode == 'veVc',
00261                         'Value is %s' % field.generateMode)
00262         self.failUnless(field.force == '', 'Value is %s' % field.force)
00263         self.failUnless(field.type == 'image', 'Value is %s' % field.type)
00264         self.failUnless(isinstance(field.storage, AnnotationStorage),
00265                         'Value is %s' % type(field.storage))
00266         self.failUnless(field.getLayerImpl('storage') == AnnotationStorage(migrate=True),
00267                         'Value is %s' % field.getLayerImpl('storage'))
00268         self.failUnless(ILayerContainer.isImplementedBy(field))
00269         self.failUnless(field.validators == "(('isNonEmptyFile', V_REQUIRED), ('checkImageMaxSize', V_REQUIRED))",
00270                         'Value is %s' % str(field.validators))
00271         self.failUnless(isinstance(field.widget, ImageWidget),
00272                         'Value is %s' % id(field.widget))
00273         vocab = field.Vocabulary(dummy)
00274         self.failUnless(isinstance(vocab, DisplayList),
00275                         'Value is %s' % type(vocab))
00276         self.failUnless(tuple(vocab) == (), 'Value is %s' % str(tuple(vocab)))
00277         self.failUnless(field.primary == 1, 'Value is %s' % field.primary)
00278 
00279 tests.append(TestATImageFields)
00280 
00281 class TestATImageFunctional(atctftestcase.ATCTIntegrationTestCase):
00282     
00283     portal_type = 'Image'
00284     views = ('image_view', 'download', 'atct_image_transform')
00285 
00286     def afterSetUp(self):
00287         atctftestcase.ATCTIntegrationTestCase.afterSetUp(self)
00288         self.obj.setImage(TEST_GIF, content_type="image/gif")
00289         dcEdit(self.obj)
00290 
00291     def test_url_returns_image(self):
00292         response = self.publish(self.obj_path, self.basic_auth)
00293         self.assertStatusEqual(response.getStatus(), 200) # OK
00294         
00295     def test_bobo_hook_security(self):
00296         # Make sure that users with 'View' permission can use the
00297         # bobo_traversed image scales, even if denied to anonymous
00298         response1 = self.publish(self.obj_path+'/image', self.basic_auth)
00299         self.assertStatusEqual(response1.getStatus(), 200) # OK
00300         # deny access to anonymous
00301         self.obj.manage_permission('View', ['Manager','Member'],0)
00302         response2 = self.publish(self.obj_path+'/image', self.basic_auth)
00303         # Should be allowed for member
00304         self.assertStatusEqual(response2.getStatus(), 200) # OK
00305         # Should fail for anonymous
00306         self.error_log._ignored_exceptions = ('Unauthorized', )
00307         response3 = self.publish(self.obj_path+'/image')
00308         self.assertStatusEqual(response3.getStatus(), 401)
00309 
00310 tests.append(TestATImageFunctional)
00311 
00312 import unittest
00313 def test_suite():
00314     suite = unittest.TestSuite()
00315     for test in tests:
00316         suite.addTest(unittest.makeSuite(test))
00317     return suite