source: products/quintagroup.transmogrifier/branches/fix_tests/quintagroup/transmogrifier/tests.py @ 2192

Last change on this file since 2192 was 2192, checked in by koval, 14 years ago

add monkey patch to support old doctest format

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