source: products/quintagroup.transmogrifier/trunk/quintagroup/transmogrifier/tests.py @ 1589

Last change on this file since 1589 was 1589, checked in by mylan, 14 years ago

Added tests of 'purge' option for portelts exporting section

File size: 40.1 KB
RevLine 
[275]1import unittest
2import pprint
3import os
4
5from zope.testing import doctest, cleanup
6from zope.component import provideUtility, provideAdapter, adapts
7from zope.interface import classProvides, implements
8
9from collective.transmogrifier.interfaces import ISectionBlueprint, ISection
10from collective.transmogrifier.tests import tearDown
11from collective.transmogrifier.sections.tests import sectionsSetUp
12from collective.transmogrifier.sections.tests import SampleSource
13
14from Products.Five import zcml
15
16import quintagroup.transmogrifier
17from quintagroup.transmogrifier.xslt import stylesheet_registry
18
19class DataPrinter(object):
20    classProvides(ISectionBlueprint)
21    implements(ISection)
22
23    def __init__(self, transmogrifier, name, options, previous):
24        self.previous = previous
25        self.printkey = [i.strip() for i in options['print'].splitlines() if i.strip()]
26        if 'prettyprint' in options:
27            self.pprint = pprint.PrettyPrinter().pprint
28
29    def __iter__(self):
30        for item in self.previous:
31            if self.printkey:
32                data = item
33                for i in self.printkey:
34                    if i in data:
35                        data = data[i]
36                    else:
37                        data = None
38                        break
39                if data is not None:
40                    if hasattr(self, 'pprint'):
41                        self.pprint(data)
42                    else:
43                        print data
44            yield item
45
46ctSectionsSetup = sectionsSetUp
47def sectionsSetUp(test):
48    ctSectionsSetup(test)
[415]49    import Products.Five
[275]50    import Products.GenericSetup
[415]51    import zope.annotation
52    zcml.load_config('meta.zcml', Products.Five)
[706]53    zcml.load_config('permissions.zcml', Products.Five)
[275]54    zcml.load_config('meta.zcml', Products.GenericSetup)
[415]55    zcml.load_config('configure.zcml', zope.annotation)
[275]56    zcml.load_config('configure.zcml', quintagroup.transmogrifier)
57
[280]58    from Products.CMFCore import utils
[1563]59    def getToolByName(context, tool_id, default=None):
[280]60        return context
61    utils.getToolByName = getToolByName
62
63    import Acquisition
64    def aq_base(obj):
65        return obj
66    Acquisition.aq_base = aq_base
67
[275]68    provideUtility(DataPrinter,
69        name=u'quintagroup.transmogrifier.tests.dataprinter')
70
71def siteWalkerSetUp(test):
72    sectionsSetUp(test)
73
74    from Products.CMFCore.interfaces import IFolderish
75    from Products.Archetypes.interfaces import IBaseFolder
76
77    class MockContent(object):
78        path = ()
79
80        def getPhysicalPath(self):
81            return self.path
82
83        def getPortalTypeName(self):
84            return self.__class__.__name__
85
86    class Document(MockContent):
87        pass
88
89    class Folder(MockContent, dict):
90        implements(IBaseFolder)
91
92        contentItems = dict.items
93        contentValues = dict.values
94
95    class MockPortal(MockContent, dict):
96        implements(IFolderish)
97
98        contentItems = dict.items
99        contentValues = dict.values
100
101    portal = MockPortal()
102
103    test.globs['plone'] = portal
104    test.globs['transmogrifier'].context = test.globs['plone']
105
106    portal.path = ('', 'plone')
107    portal['document1'] = Document()
108    portal['document1'].path = ('', 'plone', 'document1')
109    portal['folder1'] = Folder()
110    portal['folder1'].path = ('', 'plone', 'folder1')
111    portal['folder1']['document2'] = Document()
112    portal['folder1']['document2'].path = ('', 'plone', 'folder1', 'document2')
113    portal['folder1']['folder2'] = Folder()
114    portal['folder1']['folder2'].path = ('', 'plone', 'folder1', 'folder2')
115    portal['document3'] = Document()
116    portal['document3'].path = ('', 'plone', 'document3')
117
118def manifestSetUp(test):
119    sectionsSetUp(test)
120
[283]121    root = dict(
122        _path='',
123        _entries=(
124            ('news', 'Folder'),
125            ('events', 'Folder'),
[430]126            ('front-page', 'Document'),
127            ('only-in-manifest', 'Document')
[283]128        )
129    )
[275]130
[283]131    news = dict(
132        _path='news',
133        _entries=(
134            ('aggregator', 'Topic'),
[430]135            ('once-more', 'File')
[283]136        )
137    )
138
139    aggregator = dict(
140        _path='news/aggregator',
141    )
142
143    events = dict(
144        _path='events'
145    )
146
147    front_page = dict(
148        _path='front-page',
149    )
150
151    members = dict(
[430]152        _path='Members'
[283]153    )
154
[275]155    class ManifestSource(SampleSource):
156        classProvides(ISectionBlueprint)
157        implements(ISection)
158
159        def __init__(self, *args, **kw):
160            super(ManifestSource, self).__init__(*args, **kw)
[283]161            self.sample = (root, dict(), news, aggregator, events, front_page, members)
[275]162
163    provideUtility(ManifestSource,
164        name=u'quintagroup.transmogrifier.tests.manifestsource')
165
166def marshallSetUp(test):
167    sectionsSetUp(test)
168
169    from Products.Archetypes.interfaces import IBaseObject
170
[706]171    class Field(object):
172        def __init__(self, name):
173            self.name = name
174            self.obj = None
175
176        def getAccessor(self, obj):
177            self.obj = obj
178            return self
179
180        def getMutator(self, obj):
181            self.obj = obj
182            return self
183
184        def __call__(self, value=None):
185            if value is None:
186                return self.obj.fields[self.name]
187            else:
188                self.obj.fields[self.name] = value
189
[364]190    class MockBase(object):
[706]191        def __init__(self, effective=None):
192            self.fields = {
193                'effectiveDate': effective,
194                'modification_date': 'changed',
195            }
196
[364]197        def checkCreationFlag(self):
198            return True
199
200        def unmarkCreationFlag(self):
201            pass
202
203        def at_post_create_script(self):
204            pass
205
206        def at_post_edit_script(self):
207            pass
208
209        indexed = ()
210        def indexObject(self):
211            self.indexed += (self._last_path,)
212
[706]213        def getField(self, fname):
214            return Field(fname)
215
[364]216    class MockCriterion(MockBase):
[275]217        implements(IBaseObject)
[278]218        _last_path = None
219        indexed = ()
220        def indexObject(self):
221            self.indexed += (self._last_path,)
[275]222
[364]223    class MockPortal(MockBase):
[275]224        implements(IBaseObject)
225
[706]226        criterion = MockCriterion('not changed')
[278]227
[275]228        _last_path = None
229        def unrestrictedTraverse(self, path, default):
230            if path[0] == '/':
231                return default # path is absolute
232            if isinstance(path, unicode):
233                return default
234            if path == 'not/existing/bar':
235                return default
236            if path == 'topic/criterion':
237                self._last_path = path
[278]238                self.criterion._last_path = path
239                return self.criterion
[275]240            if path.endswith('/notatcontent'):
241                return object()
242            self._last_path = path
243            return self
244
245        def getId(self):
246            return "plone"
247
[278]248        indexed = ()
249        def indexObject(self):
250            self.indexed += (self._last_path,)
251
[486]252        updatedRoles = False
253        def updateRoleMappings(self):
254            self.updatedRoles = True
255
256        reindexed = False
257        def reindexIndex(self, name, extra=None):
258            self.reindexed = True
259
[278]260        marshalled = ()
[275]261        def marshall(self, instance, **kwargs):
[278]262            self.marshalled += ((self._last_path, kwargs.get('atns_exclude')),)
[275]263            # Marshall often fails to export topic criteria
264            if isinstance(instance, MockCriterion):
265                return None, None, None
266            else:
267                return None, None, "marshalled"
268
[278]269        demarshalled = ()
270        def demarshall(self, instance, data):
271            # we don't need to test Marshall product, only check if we call it's components
272            self.demarshalled += (self._last_path,)
273
[275]274    portal = MockPortal()
275    test.globs['plone'] = portal
276    test.globs['transmogrifier'].context = test.globs['plone']
277
278    from Products.Marshall import registry
279    def getComponent(name):
280        return portal
281    registry.getComponent = getComponent
282
283    class MarshallSource(SampleSource):
284        classProvides(ISectionBlueprint)
285        implements(ISection)
286
287        def __init__(self, *args, **kw):
288            super(MarshallSource, self).__init__(*args, **kw)
289            self.sample = (
[278]290                dict(),
291                dict(_path='spam/eggs/foo', _excluded_fields=('file', 'image')),
[275]292                dict(_path='topic/criterion'),
293                dict(_path='not/existing/bar'),
[278]294                dict(_path='spam/eggs/notatcontent', 
295                     _files=dict(marshall=dict(data='xml', name='.marshall.xml'))),
[275]296            )
297    provideUtility(MarshallSource,
298        name=u'quintagroup.transmogrifier.tests.marshallsource')
299
300def propertyManagerSetUp(test):
301    sectionsSetUp(test)
302
303    from OFS.interfaces import IPropertyManager
304
305    class MockPortal(object):
306        implements(IPropertyManager)
307
[279]308        _properties = (
309            {'id':'title', 'type': 'string', 'mode': 'w'},
310            {'id':'description', 'type': 'string', 'mode': 'w'},
311            {'id':'encoding', 'type': 'string', 'mode': 'w'},
312            {'id':'author', 'type': 'string', 'mode': 'w'}
313        )
314
[275]315        _last_path = None
316        def unrestrictedTraverse(self, path, default):
317            if path[0] == '/':
318                return default # path is absolute
319            if isinstance(path, unicode):
320                return default
321            if path == 'not/existing/bar':
322                return default
323            if path.endswith('/notatcontent'):
324                return object()
325            self._last_path = path
326            return self
327
328        def _propertyMap(self):
[279]329            return self._properties
[275]330
[279]331        def getProperty(self, id, d=None):
[275]332            return 'value'
333
[279]334        def propdict(self):
335            d={}
336            for p in self._properties:
337                d[p['id']]=p
338            return d
339
340        updated = ()
341        def _updateProperty(self, id, value):
[453]342            self.updated += ((self._last_path, id, value))
[279]343
[275]344    portal = MockPortal()
345    test.globs['plone'] = portal
346    test.globs['transmogrifier'].context = test.globs['plone']
347
348    class PropertyManagerSource(SampleSource):
349        classProvides(ISectionBlueprint)
350        implements(ISection)
351
352        def __init__(self, *args, **kw):
353            super(PropertyManagerSource, self).__init__(*args, **kw)
354            self.sample = (
[279]355                dict(),
[275]356                dict(_path='not/existing/bar'),
357                dict(_path='spam/eggs/notatcontent'),
[279]358                dict(_path='spam/eggs/foo', _excluded_properties=('encoding',)),
[275]359            )
360
361    provideUtility(PropertyManagerSource,
362        name=u'quintagroup.transmogrifier.tests.propertymanagersource')
363
[280]364def commentsSetUp(test):
[275]365    sectionsSetUp(test)
366
367    class MockDiscussionItem(object):
[280]368        creator = 'creator'
369        modified = 'date'
370
371        def __init__(self, reply, text=""):
[275]372            self.in_reply_to = reply
373            self.text = text
374
[280]375        def __of__(self, container):
376            return self
377
378        def getMetadataHeaders(self):
379            return []
380
381        def setMetadata(self, headers):
382            pass
383
[275]384        def Creator(self):
[280]385            return self.creator
[275]386
[280]387        def addCreator(self, creator):
388            self.creator = creator
389
[275]390        def ModificationDate(self):
[280]391            return self.modified
[275]392
[280]393        def setModificationDate(self, date):
394            self.modified = date
[275]395
[280]396        def setFormat(self, format):
397            pass
398
399        def _edit(self, text=None):
400            self.text = text
401
402        def indexObject(self):
403            pass
404
405        def __repr__(self):
406            return "<DicussionItem %s %s %s %s>" % (
407                self.Creator(),
408                self.ModificationDate(),
409                self.in_reply_to,
410                self.text
411                )
412
413    from Products.CMFDefault import DiscussionItem
414    DiscussionItem.DiscussionItem = MockDiscussionItem
415
[275]416    class MockPortal(object):
[280]417        _discussion = {
[275]418            '1': MockDiscussionItem(None, 'comment to content'),
419            '2': MockDiscussionItem('1', 'reply to first comment'),
420            '3': MockDiscussionItem(None, 'other comment to content')
421        }
[280]422        _container = {}
[275]423
424        @property
425        def talkback(self):
426            return self
427
428        def objectItems(self):
[280]429            l = self._discussion.items()
[275]430            l.sort(key=lambda x: int(x[0]))
431            return l
432
433        def unrestrictedTraverse(self, path, default):
434            if path[0] == '/':
435                return default # path is absolute
436            if isinstance(path, unicode):
437                return default
438            if path == 'not/existing/bar':
439                return default
440            if path.endswith('/notdiscussable'):
441                return object()
442            return self
443
[280]444        def getDiscussionFor(self, obj):
445            return self
446
[275]447    portal = MockPortal()
448    test.globs['plone'] = portal
449    test.globs['transmogrifier'].context = test.globs['plone']
450
[280]451    class CommentsSource(SampleSource):
[275]452        classProvides(ISectionBlueprint)
453        implements(ISection)
454
455        def __init__(self, *args, **kw):
[280]456            super(CommentsSource, self).__init__(*args, **kw)
[275]457            self.sample = (
[280]458                dict(),
[275]459                dict(_path='not/existing/bar'),
460                dict(_path='spam/eggs/notdiscussable'),
[280]461                dict(_path='spam/eggs/foo'),
[275]462            )
463
[280]464    provideUtility(CommentsSource,
465        name=u'quintagroup.transmogrifier.tests.commentssource')
[275]466
467
[277]468def dataCorrectorSetUp(test):
[275]469    sectionsSetUp(test)
470
471    class MockPortal(object):
472        def unrestrictedTraverse(self, path, default):
473            if path[0] == '/':
474                return default # path is absolute
475            if isinstance(path, unicode):
476                return default
477            if path == 'not/existing/bar':
478                return default
479            if path.endswith('/notadaptable'):
480                return object()
481            return self
482
483    portal = MockPortal()
484    test.globs['plone'] = portal
485    test.globs['transmogrifier'].context = test.globs['plone']
486
[1446]487    from collective.transmogrifier.interfaces import ITransmogrifier
[277]488    from quintagroup.transmogrifier.interfaces import IExportDataCorrector, \
489        IImportDataCorrector
[275]490
[277]491    class MockExportAdapter(object):
[275]492        implements(IExportDataCorrector)
[1446]493        adapts(MockPortal, ITransmogrifier)
494        def __init__(self, context, transmogrifier):
[275]495            self.context = context
[1446]496            self.transmogrifier = transmogrifier
[275]497
498        def __call__(self, data):
[277]499            return "modified export data"
[275]500
[277]501    provideAdapter(MockExportAdapter, name="marshall")
[275]502
[277]503    class MockImportAdapter(object):
504        implements(IImportDataCorrector)
[1446]505        adapts(MockPortal, ITransmogrifier)
506        def __init__(self, context, transmogrifier):
[277]507            self.context = context
[1446]508            self.transmogrifier = transmogrifier
[277]509
510        def __call__(self, data):
511            return "modified import data"
512
513    provideAdapter(MockImportAdapter, name="manifest")
514
515    class DataCorrectorSource(SampleSource):
[275]516        classProvides(ISectionBlueprint)
517        implements(ISection)
518
519        def __init__(self, *args, **kw):
[277]520            super(DataCorrectorSource, self).__init__(*args, **kw)
[275]521            self.sample = (
[277]522                dict(),
523                dict(_files=dict(marshall="item hasn't path")),
524                dict(_path='spam/eggs/foo'),
525                dict(_path='not/existing/bar'),
526                dict(_path='spam/eggs/notadaptable', _files=dict(marshall="object isn't adaptable")),
[275]527                dict(_path='spam/eggs/foo',
[277]528                     _files=dict(marshall='marshall data', unchanged='this must be unchanged')),
529                dict(_path='spam/eggs/foo',
530                     _files=dict(manifest='manifest data', unchanged='this must be unchanged')),
[275]531            )
532
[277]533    provideUtility(DataCorrectorSource,
534        name=u'quintagroup.transmogrifier.tests.datacorrectorsource')
[275]535
536def writerSetUp(test):
537    sectionsSetUp(test)
538
539    class MockExportContext(object):
540        def __init__( self, *args, **kwargs):
541            self.args = args
542            for k, v in kwargs.items():
543                setattr(self, k, v)
544            self._wrote = []
545
546        def __getitem__(self, name):
547            return getattr(self, name, None)
548
549        def __contains__(self, name):
550            return hasattr(self, name)
551
552        def writeDataFile(self, filename, text, content_type, subdir=None):
553            filename = '%s/%s' % (subdir, filename)
554            self._wrote.append((filename, text, content_type))
555
556        def __repr__(self):
557            s = " ".join(["%s=%s" % (k,v) for k,v in self.__dict__.items()])
558            return "<%s %s>" % (self.__class__.__name__, s)
559
560
561    from Products.GenericSetup import context
562
563    context.DirectoryExportContext = type('Directory', (MockExportContext,), {})
564    context.TarballExportContext = type('Tarball', (MockExportContext,), {})
565    context.SnapshotExportContext = type('Snapshot', (MockExportContext,), {})
566
567    class WriterSource(SampleSource):
568        classProvides(ISectionBlueprint)
569        implements(ISection)
570
571        def __init__(self, *args, **kw):
572            super(WriterSource, self).__init__(*args, **kw)
573            self.sample = (
574                dict(_path='spam/eggs/foo'),
575                dict(_files=dict(mock=dict(name='.first.xml', data='some data'))),
576                dict(_path='spam/eggs/foo',
577                     _files=dict(mock=dict(name='.first.xml', data='some data'),
578                                 other=dict(name='.second.xml', data='other data'))),
579                dict(_path='other/path',
580                     _files=dict(mock=dict(name='.third.xml', data='some data')))
581            )
582
583    provideUtility(WriterSource,
584        name=u'quintagroup.transmogrifier.tests.writersource')
585
586    class SingleItemSource(SampleSource):
587        classProvides(ISectionBlueprint)
588        implements(ISection)
589
590        def __init__(self, *args, **kw):
591            super(SingleItemSource, self).__init__(*args, **kw)
592            self.sample = (
593                dict(_path='', _files={}),
594            )
595
596    provideUtility(SingleItemSource,
597        name=u"quintagroup.transmogrifier.tests.singleitemsource")
598
599def readerSetUp(test):
600    sectionsSetUp(test)
601
602    class MockImportContext(object):
603
604        _dirs = [
605            'structure',
606            'structure/news', 'structure/news/recent',
607            'structure/pages', 'structure/pages/front-page',
608        ]
609        _files = [
610            'structure/.properties.xml',
611            'structure/other.file',
612            'structure/news/.objects.xml',
613            'structure/pages/.objects.xml',
614            'structure/pages/front-page/.marshall.xml',
615            'structure/pages/front-page/.comments.xml',
616        ]
617
618        def __init__( self, *args, **kwargs):
619            self.args = args
620            for k, v in kwargs.items():
621                setattr(self, k, v)
622
623        def __repr__(self):
624            s = " ".join(["%s=%s" % (k,v) for k,v in self.__dict__.items()])
625            return "<%s %s>" % (self.__class__.__name__, s)
626
627        def readDataFile(self, filename, subdir=None):
628            return 'some data'
629
630        def isDirectory(self, path):
631            return path == '' or path in self._dirs
632
633        def listDirectory(self, path):
634            all_names = self._dirs + self._files
635            if path:
636                pfx_len = len(path)+1
637            else:
638                pfx_len = 0
639            names = []
640            for name in all_names:
641                if name == path:
642                    continue
643                if not name.startswith(path):
644                    continue
645                name = name[pfx_len:]
646                if '/' in name:
647                    continue
648                names.append(name)
649            return names
650
651    from Products.GenericSetup import context
652
653    context.DirectoryImportContext = type('Directory', (MockImportContext,),
654        {'listDirectory': lambda self, path: []})
655    context.TarballImportContext = type('Tarball', (MockImportContext,), {})
656    context.SnapshotImportContext = type('Snapshot', (MockImportContext,),
657        {'listDirectory': lambda self, path: []})
658
659def substitutionSetUp(test):
660    sectionsSetUp(test)
661
662    class SubstitutionSource(SampleSource):
663        classProvides(ISectionBlueprint)
664        implements(ISection)
665
666        def __init__(self, *args, **kw):
667            super(SubstitutionSource, self).__init__(*args, **kw)
668            self.sample = (
669                {},
670                {'_type': 'Blog'},
671                {'_type': 'PloneFormMailer'},
672                {'_type': 'Document'},
673            )
674
675    provideUtility(SubstitutionSource,
676        name=u'quintagroup.transmogrifier.tests.substitutionsource')
677
678class MetaDirectivesTests(unittest.TestCase):
679    def setUp(self):
680        zcml.load_config('meta.zcml', quintagroup.transmogrifier)
681
682    def tearDown(self):
683        stylesheet_registry.clear()
684        cleanup.cleanUp()
685
686    def testEmptyZCML(self):
687        zcml.load_string('''\
688<configure xmlns:transmogrifier="http://namespaces.plone.org/transmogrifier">
689</configure>''')
690        self.assertEqual(stylesheet_registry.listStylesheetNames(), ())
691
692    def testConfigZCML(self):
693        zcml.load_string('''\
694<configure
695    xmlns:transmogrifier="http://namespaces.plone.org/transmogrifier">
696<transmogrifier:stylesheet
697    source="marshall"
698    from="Blog"
699    to="Weblog"
700    file="blog.xsl"
701    />
702</configure>''')
703        self.assertEqual(stylesheet_registry.listStylesheetNames(),
704                         (u'marshall:Blog:Weblog',))
705        path = os.path.split(quintagroup.transmogrifier.__file__)[0]
706        self.assertEqual(
707            stylesheet_registry.getStylesheet('marshall', 'Blog', 'Weblog'),
708            dict(from_=u'Blog',
709                 to=u'Weblog',
710                 file=os.path.join(path, 'blog.xsl'))
711        )
712
713    def testMultipleZCML(self):
714        zcml.load_string('''\
715<configure
716    xmlns:transmogrifier="http://namespaces.plone.org/transmogrifier">
717<transmogrifier:stylesheet
718    source="marshall"
719    from="Blog"
720    to="Weblog"
721    file="blog.xsl"
722    />
723<transmogrifier:stylesheet
724    source="propertymanager"
725    from="BlogEntry"
726    to="WeblogEntry"
727    file="blogentry.xsl"
728    />
729</configure>''')
730        self.assertEqual(stylesheet_registry.listStylesheetNames(),
731                         (u'marshall:Blog:Weblog', u'propertymanager:BlogEntry:WeblogEntry'))
732
733def xsltSetUp(test):
734    sectionsSetUp(test)
735
736    class XSLTSource(SampleSource):
737        classProvides(ISectionBlueprint)
738        implements(ISection)
739
740        def __init__(self, *args, **kw):
741            super(XSLTSource, self).__init__(*args, **kw)
742            self.sample = (
743                {},
744                {'_type': 'Weblog'},
745                {'_old_type': 'Blog'},
746                {'_old_type': 'Blog',
747                 '_type': 'Weblog',
[277]748                 '_files': {'manifest': {'data': 'xml', 'name': 'manifest.xml'}}},
[275]749                {'_old_type': 'Blog',
750                 '_type': 'Weblog',
751                 '_files': {'marshall': {'data': 'xml', 'name': 'marshall.xml'}}},
752            )
753
754    provideUtility(XSLTSource,
755        name=u'quintagroup.transmogrifier.tests.xsltsource')
756
757    from quintagroup.transmogrifier.xslt import XSLTSection, stylesheet_registry
758
[277]759    XSLTSection.applyTransformations = lambda self, xml, xslt: 'transformed xml'
760    test.globs['stylesheet_registry'] = stylesheet_registry
[275]761
[370]762def binarySetUp(test):
763    sectionsSetUp(test)
764
765    from Products.Archetypes.interfaces import IBaseObject
766
767    class MockPortal(object):
768        implements(IBaseObject)
769
770        _last_path = None
771        def unrestrictedTraverse(self, path, default):
772            if path[0] == '/':
773                return default # path is absolute
774            if isinstance(path, unicode):
775                return default
776            if path == 'not/existing/bar':
777                return default
778            if path.endswith('/notatcontent'):
779                return object()
780            self._last_path = path
781            return self
782
783        fields = ['id', 'title', 'file', 'image']
784
785        def Schema(self):
786            return dict.fromkeys(self.fields)
787
788        def isBinary(self, field):
[453]789            return field in ('file', 'image')
[370]790
[453]791        _current_field = None
[370]792        def getField(self, field):
[453]793            self._current_field = field
[370]794            return self
795
796        def getBaseUnit(self, obj):
797            return self
798
799        def getFilename(self):
[453]800            if self._current_field == 'file':
801                return 'archive.tar.gz'
802            else:
803                return ''
[370]804
805        def getContentType(self):
[453]806            if self._current_field == 'file':
807                return 'application/x-tar'
808            else:
809                return 'image/png'
[370]810
811        def getRaw(self):
[453]812            if self._current_field == 'file':
813                return 'binary data'
814            else:
[1446]815                return 'image'
[370]816
817        def getMutator(self, obj):
818            return self
819
820        updated = ()
821        def __call__(self, data, filename=None, mimetype=None):
822            self.updated += (filename, mimetype, data)
823
824    portal = MockPortal()
825    test.globs['plone'] = portal
826    test.globs['transmogrifier'].context = test.globs['plone']
827
828    class BinarySource(SampleSource):
829        classProvides(ISectionBlueprint)
830        implements(ISection)
831
832        def __init__(self, *args, **kw):
833            super(BinarySource, self).__init__(*args, **kw)
834            self.sample = (
835                dict(),
836                dict(_path='not/existing/bar'),
837                dict(_path='spam/eggs/notatcontent'),
838                dict(_path='spam/eggs/foo'),
839            )
840
841    provideUtility(BinarySource,
842        name=u'quintagroup.transmogrifier.tests.binarysource')
843
[371]844from DateTime import DateTime
845
846def catalogSourceSetUp(test):
847    sectionsSetUp(test)
848
849    class MockContent(dict):
850        def __init__(self, **kw):
851            self.update(kw)
[417]852            self['id'] = self.getId
[371]853
854        def getPath(self):
855            return self['path']
856
857        @property
858        def getId(self):
859            path = self.getPath()
860            return path.rsplit('/', 1)[-1]
861
862        @property
[428]863        def portal_type(self):
[371]864            return self['portal_type']
865
[373]866        @property
867        def is_folderish(self):
868            return self['portal_type'] == 'Folder' and True or False
869
[371]870    class MockPortal(dict):
871
872        content = ()
873        def __call__(self, **kw):
874            res = []
875            for obj in self.content:
[373]876                matched = True
[371]877                for index, query in kw.items():
878                    if index not in obj:
[373]879                        matched = False
880                        break
881                    if matched and index == 'modified':
[371]882                        if isinstance(query, dict):
883                            value = query['query']
884                            range_ = query['range']
885                            if range_ == 'min' and DateTime(obj[index]) >= DateTime(value):
886                                matched = True
887                            elif range_ == 'max' and DateTime(obj[index]) <= DateTime(value):
888                                matched = True
889                            else:
890                                matched = False
891                        else:
892                            if DateTime(obj[index]) == DateTime(query):
893                                matched = True
894                            else:
895                                matched = False
896                    elif matched and index == 'path':
897                        if obj[index].startswith(query):
898                            matched = True
899                        else:
900                            matched = False
901                    elif matched:
902                        if obj[index] == query:
903                            matched = True
904                        else:
905                            matched = False
906                if matched:
907                    res.append(obj)
908
909            return res
910
911    portal = MockPortal()
912    doc1 = MockContent(path='/plone/document1', portal_type='Document',
[373]913        modified='2008-11-01T12:00:00Z')
[371]914    folder1 = MockContent(path='/plone/folder1', portal_type='Folder',
[416]915        modified='2008-11-01T12:00:00Z')
[371]916    doc2 = MockContent(path='/plone/folder1/document2', portal_type='Document',
[373]917        modified='2008-11-02T12:00:00Z')
[371]918    doc3 = MockContent(path='/plone/folder1/document3', portal_type='Document',
[416]919        modified='2008-11-02T12:00:00Z')
920    folder2 = MockContent(path='/plone/folder2', portal_type='Folder',
921        modified='2008-11-02T12:00:00Z')
922    doc4 = MockContent(path='/plone/folder2/document4', portal_type='Document',
[417]923        modified='2008-11-01T12:00:00Z')
924    comment = MockContent(path='/plone/folder2/document4/talkback/1234567890', portal_type='Discussion Item',
[416]925        modified='2008-11-02T12:00:00Z')
[373]926    # items are sorted on their modification date
[417]927    portal.content = (doc1, folder1, folder2, doc2, doc3, doc4, comment)
[371]928
929    test.globs['plone'] = portal
930    test.globs['transmogrifier'].context = test.globs['plone']
931
[1381]932def flushCacheSetUp(test):
933    sectionsSetUp(test)
934   
935    class DataBase(object):
936        def __init__(self, context):
937            self.context = context
938       
939        def cacheMinimize(self):
940            self.context.cacheMinimized += 1
941
942        def _getDB(self):
943            return self
944   
945    class DataBasePanel(object):
946        def __init__(self, context):
947            self.context = context
948       
949        def getDatabaseNames(self):
950            return ('main',)
951       
952        def __getitem__(self, key):
953            return DataBase(self.context)
954   
955    class ControlPanel(object):
956        def __init__(self, context):
957            self.Database = DataBasePanel(context)
958   
959    class MockPortal(object):
960        def __init__(self):
961            self.cacheMinimized = 0
962            self.Control_Panel = ControlPanel(self)
963       
964    test.globs['plone'] = MockPortal()
965    test.globs['transmogrifier'].context = test.globs['plone']
966
[1478]967
968def interfaceManagerSetUp(test):
969    sectionsSetUp(test)
970
[1539]971    from zope.interface import Interface
[1478]972    from zope.annotation.interfaces import IAttributeAnnotatable
973    from zope.interface import alsoProvides as orig_alsoProvides
974    from Products.Archetypes.interfaces import IBaseObject
975
976    class MockPortal(object):
977
978        implements(
979            IBaseObject,
980        )
981
982        _last_path = None
983        def unrestrictedTraverse(self, path, default):
984            if path[0] == '/':
985                return default # path is absolute
986            if isinstance(path, unicode):
987                return default
988            if path == 'not/existing/bar':
989                return default
990            if path.endswith('/notatcontent'):
991                return object()
992            self._last_path = path
993            return self
994
[1486]995        # implement portal_catalog reindex method
996        def reindexIndex(self, *args, **kwargs):
997            pass
[1478]998
[1486]999
[1478]1000    updated = []
1001    test.globs['updated'] = updated
1002    def patch_alsoProvides(object, *interfaces):
1003        updated.extend([i.__identifier__ for i in interfaces])
1004        orig_alsoProvides(object, *interfaces)
1005    quintagroup.transmogrifier.interfacemanager.alsoProvides = patch_alsoProvides
1006
1007    portal = MockPortal()
[1539]1008    orig_alsoProvides(portal, IAttributeAnnotatable, Interface)
[1478]1009    test.globs['plone'] = portal
1010    test.globs['transmogrifier'].context = test.globs['plone']
1011
1012    class InterfaceManagerSource(SampleSource):
1013        classProvides(ISectionBlueprint)
1014        implements(ISection)
1015
1016        def __init__(self, *args, **kw):
1017            super(InterfaceManagerSource, self).__init__(*args, **kw)
1018            self.sample = (
1019                dict(),
1020                dict(_path='not/existing/bar'),
1021                dict(_path='spam/eggs/notatcontent'),
1022                dict(_path='spam/eggs/foo'),
1023            )
1024
1025    provideUtility(InterfaceManagerSource,
1026        name=u'quintagroup.transmogrifier.tests.interfacemanagersource')
1027
[1563]1028def portletsSetUp(test):
1029    sectionsSetUp(test)
[1478]1030
[1563]1031    from zope.interface import Interface
1032    from zope.annotation.interfaces import IAttributeAnnotatable
1033    from plone.portlets.interfaces import ILocalPortletAssignable
1034
1035    # this bases class adds __of__ method
1036    from Acquisition import Implicit
1037
1038    class MockPortal(Implicit):
1039        implements(IAttributeAnnotatable, ILocalPortletAssignable)
1040
1041        _last_path = None
1042        def unrestrictedTraverse(self, path, default):
1043            if path[0] == '/':
1044                return default # path is absolute
1045            if isinstance(path, unicode):
1046                return default
1047            if path == 'not/existing/bar':
1048                return default
1049            if path.endswith('/notassignable'):
1050                return object()
1051            self._last_path = path
1052            return self
1053
1054        def getPhysicalPath(self):
1055            return [''] + self._last_path.split('/')
1056
1057    portal = MockPortal()
1058    test.globs['plone'] = portal
1059    test.globs['transmogrifier'].context = test.globs['plone']
1060
1061    class PortletsSource(SampleSource):
1062        classProvides(ISectionBlueprint)
1063        implements(ISection)
1064
1065        def __init__(self, *args, **kw):
1066            super(PortletsSource, self).__init__(*args, **kw)
1067            self.sample = (
1068                dict(),
1069                dict(_path='not/existing/bar'),
1070                dict(_path='spam/eggs/notassignable'),
1071                dict(_path='assignable'),
[1564]1072                dict(_path='other-assignable', 
1073                    files=dict(portlets=dict(
1074                        name='.portlets.xml',
1075                        data="""<?xml version="1.0" encoding="utf-8"?>
1076<portlets>
1077  <assignment category="context" key="/other-assignable" manager="plone.leftcolumn" name="habra-rss" type="portlets.rss">
1078    <property name="count">
1079      20
1080    </property>
1081    <property name="url">
1082      http://habrahabr.ru/rss/
1083    </property>
1084    <property name="portlet_title">
1085      Habrahabr RSS feed
1086    </property>
1087    <property name="timeout">
1088      120
1089    </property>
1090  </assignment>
[1588]1091  <blacklist category="user" manager="plone.leftcolumn" status="block"/>
1092  <blacklist category="group" manager="plone.leftcolumn" status="acquire"/>
1093  <blacklist category="content_type" manager="plone.leftcolumn" status="acquire"/>
1094  <blacklist category="context" manager="plone.leftcolumn" status="acquire"/>
[1564]1095</portlets>
1096""")
1097                    )
1098                )
[1563]1099            )
1100
1101    provideUtility(PortletsSource,
1102        name=u'quintagroup.transmogrifier.tests.portletssource')
1103
[1589]1104    class PortletsSource2(SampleSource):
1105        classProvides(ISectionBlueprint)
1106        implements(ISection)
1107
1108        def __init__(self, *args, **kw):
1109            super(PortletsSource2, self).__init__(*args, **kw)
1110            self.sample = (
1111                dict(),
1112                dict(_path='other-assignable',
1113                    files=dict(portlets=dict(
1114                        name='.portlets.xml',
1115                        data="""<?xml version="1.0" encoding="utf-8"?>
1116<portlets>
1117  <assignment category="context" key="/other-assignable" manager="plone.leftcolumn" name="sumno-rss-2" type="portlets.rss">
1118    <property name="count">
1119      30
1120    </property>
1121    <property name="url">
1122      http://sumno.com/rss
1123    </property>
1124    <property name="portlet_title">
1125      Sumno RSS feed
1126    </property>
1127    <property name="timeout">
1128      360
1129    </property>
1130  </assignment>
1131  <blacklist category="user" manager="plone.leftcolumn" status="block"/>
1132  <blacklist category="group" manager="plone.leftcolumn" status="acquire"/>
1133  <blacklist category="content_type" manager="plone.leftcolumn" status="acquire"/>
1134  <blacklist category="context" manager="plone.leftcolumn" status="acquire"/>
1135</portlets>
1136""")
1137                    )
1138                )
1139            )
1140    provideUtility(PortletsSource2,
1141       name=u'quintagroup.transmogrifier.tests.portletssource2')
1142
[1563]1143    # prepare the one portlet for testing
1144    from zope.interface import alsoProvides
1145    from zope.component import getUtility, getMultiAdapter
1146    from zope.component.interfaces import IFactory
1147    from zope.component.factory import Factory
1148
1149    from plone.portlets.manager import PortletManager
1150    from plone.portlets.interfaces import IPortletManager, IPortletType, \
1151        IPortletAssignmentMapping
1152    from plone.portlets.registration import PortletType
1153
1154    from plone.app.portlets.assignable import localPortletAssignmentMappingAdapter
[1588]1155    from plone.portlets.assignable import LocalPortletAssignmentManager
[1563]1156    from plone.app.portlets.interfaces import IPortletTypeInterface
[1588]1157    from plone.portlets.interfaces import  ILocalPortletAssignmentManager
1158    from plone.portlets.constants import USER_CATEGORY
[1563]1159    # from plone.app.portlets.browser.interfaces import IPortletAdding
1160    from plone.app.portlets.portlets.rss import IRSSPortlet, Assignment #, Renderer, AddForm, EditForm
1161
1162    # register portlet manager and assignment mapping adapter
1163    manager = PortletManager()
1164    provideUtility(manager, IPortletManager, name='plone.leftcolumn')
1165    provideAdapter(localPortletAssignmentMappingAdapter)
[1588]1166    provideAdapter(LocalPortletAssignmentManager)
[1563]1167    mapping = getMultiAdapter((portal, manager), IPortletAssignmentMapping)
[1588]1168    assignable = getMultiAdapter((portal, manager), ILocalPortletAssignmentManager)
[1564]1169    test.globs['mapping'] = mapping
[1588]1170    test.globs['assignable'] = assignable
[1563]1171
1172    # register portlet (this is what plone:portlet zcml directive does)
1173    PORTLET_NAME = 'portlets.rss'
1174    alsoProvides(IRSSPortlet, IPortletTypeInterface)
1175    provideUtility(provides=IPortletTypeInterface, name=PORTLET_NAME, component=IRSSPortlet)
1176    provideUtility(provides=IFactory, name=PORTLET_NAME, component=Factory(Assignment))
1177
1178    # register a portlet type (this is what <portlet /> element in portlets.xml
1179    # does)
1180    portlet = PortletType()
1181    portlet.title = 'RSS Feed'
1182    portlet.description = 'A portlet which can receive and render an RSS feed.'
1183    portlet.addview = PORTLET_NAME
1184    portlet.for_ = [Interface]
1185    provideUtility(component=portlet, provides=IPortletType, 
1186        name=PORTLET_NAME)
1187
1188    # add a portlet and configure it (this is done on @@manage-portlets view)
1189    assignment = getUtility(IFactory, name=PORTLET_NAME)()
1190    mapping['rss'] = assignment
1191    portlet_interface = getUtility(IPortletTypeInterface, name=PORTLET_NAME)
1192    data = {
1193        'portlet_title': u'RSS feed',
1194        'count': 10,
1195        'url': u'http://sumno.com/feeds/main-page/',
1196        'timeout': 60
1197    }
1198    for k, v in data.items():
1199        field = portlet_interface.get(k)
1200        field = field.bind(assignment)
1201        field.validate(v)
1202        field.set(assignment, v)
[1588]1203    # set blacklists for user category to 'block'
1204    assignable.setBlacklistStatus(USER_CATEGORY, True)
[1563]1205
[275]1206def test_suite():
1207    import sys
1208    suite = unittest.findTestCases(sys.modules[__name__])
1209    suite.addTests((
1210        doctest.DocFileSuite(
1211            'sitewalker.txt',
1212            setUp=siteWalkerSetUp, tearDown=tearDown),
1213        doctest.DocFileSuite(
1214            'manifest.txt',
1215            setUp=manifestSetUp, tearDown=tearDown),
1216        doctest.DocFileSuite(
1217            'marshall.txt',
1218            setUp=marshallSetUp, tearDown=tearDown),
1219        doctest.DocFileSuite(
1220            'propertymanager.txt',
1221            setUp=propertyManagerSetUp, tearDown=tearDown),
1222        doctest.DocFileSuite(
[282]1223            'comments.txt',
[280]1224            setUp=commentsSetUp, tearDown=tearDown),
[275]1225        doctest.DocFileSuite(
[277]1226            'datacorrector.txt',
1227            setUp=dataCorrectorSetUp, tearDown=tearDown),
[275]1228        doctest.DocFileSuite(
1229            'writer.txt',
1230            setUp=writerSetUp, tearDown=tearDown),
1231        doctest.DocFileSuite(
1232            'reader.txt',
1233            setUp=readerSetUp, tearDown=tearDown),
1234        doctest.DocFileSuite(
1235            'substitution.txt',
1236            setUp=substitutionSetUp, tearDown=tearDown),
1237        doctest.DocFileSuite(
1238            'xslt.txt',
1239            setUp=xsltSetUp, tearDown=tearDown),
[370]1240        doctest.DocFileSuite(
1241            'binary.txt',
1242            setUp=binarySetUp, tearDown=tearDown),
[371]1243        doctest.DocFileSuite(
1244            'catalogsource.txt',
1245            setUp=catalogSourceSetUp, tearDown=tearDown),
[1381]1246        doctest.DocFileSuite(
1247            'flushcache.txt',
1248            setUp=flushCacheSetUp, tearDown=tearDown),
[1478]1249        doctest.DocFileSuite(
1250            'interfacemanager.txt',
1251            setUp=interfaceManagerSetUp, tearDown=tearDown),
[1563]1252        doctest.DocFileSuite(
1253            'portlets.txt',
1254            setUp=portletsSetUp, tearDown=tearDown),
[275]1255    ))
1256    return suite
Note: See TracBrowser for help on using the repository browser.