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

Last change on this file since 280 was 280, checked in by chervol, 18 years ago

fixes for ecto

File size: 23.9 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# Doctest support
20
21class DataPrinter(object):
22    classProvides(ISectionBlueprint)
23    implements(ISection)
24
25    def __init__(self, transmogrifier, name, options, previous):
26        self.previous = previous
27        self.printkey = [i.strip() for i in options['print'].splitlines() if i.strip()]
28        if 'prettyprint' in options:
29            self.pprint = pprint.PrettyPrinter().pprint
30
31    def __iter__(self):
32        for item in self.previous:
33            if self.printkey:
34                data = item
35                for i in self.printkey:
36                    if i in data:
37                        data = data[i]
38                    else:
39                        data = None
40                        break
41                if data is not None:
42                    if hasattr(self, 'pprint'):
43                        self.pprint(data)
44                    else:
45                        print data
46            yield item
47
48ctSectionsSetup = sectionsSetUp
49def sectionsSetUp(test):
50    ctSectionsSetup(test)
51    # load meta.zcml of directives that are used in out package config
52    import Products.GenericSetup
53    zcml.load_config('meta.zcml', Products.GenericSetup)
54    zcml.load_config('configure.zcml', quintagroup.transmogrifier)
55
56    from Products.CMFCore import utils
57    def getToolByName(context, tool_id):
58        return context
59    utils.getToolByName = getToolByName
60
61    import Acquisition
62    def aq_base(obj):
63        return obj
64    Acquisition.aq_base = aq_base
65
66    provideUtility(DataPrinter,
67        name=u'quintagroup.transmogrifier.tests.dataprinter')
68
69def siteWalkerSetUp(test):
70    sectionsSetUp(test)
71
72    from Products.CMFCore.interfaces import IFolderish
73    from Products.Archetypes.interfaces import IBaseFolder
74
75    class MockContent(object):
76        path = ()
77
78        def getPhysicalPath(self):
79            return self.path
80
81        def getPortalTypeName(self):
82            return self.__class__.__name__
83
84    class Document(MockContent):
85        pass
86
87    class Folder(MockContent, dict):
88        implements(IBaseFolder)
89
90        contentItems = dict.items
91        contentValues = dict.values
92
93    class MockPortal(MockContent, dict):
94        implements(IFolderish)
95
96        contentItems = dict.items
97        contentValues = dict.values
98
99    portal = MockPortal()
100
101    test.globs['plone'] = portal
102    test.globs['transmogrifier'].context = test.globs['plone']
103
104    portal.path = ('', 'plone')
105    portal['document1'] = Document()
106    portal['document1'].path = ('', 'plone', 'document1')
107    portal['folder1'] = Folder()
108    portal['folder1'].path = ('', 'plone', 'folder1')
109    portal['folder1']['document2'] = Document()
110    portal['folder1']['document2'].path = ('', 'plone', 'folder1', 'document2')
111    portal['folder1']['folder2'] = Folder()
112    portal['folder1']['folder2'].path = ('', 'plone', 'folder1', 'folder2')
113    portal['document3'] = Document()
114    portal['document3'].path = ('', 'plone', 'document3')
115
116def manifestSetUp(test):
117    sectionsSetUp(test)
118
119    item = {'_entries' : (
120        ('document1', 'Document'),
121        ('folder1', 'Folder'),
122        ('document3', 'Document'),)
123    }
124
125    class ManifestSource(SampleSource):
126        classProvides(ISectionBlueprint)
127        implements(ISection)
128
129        def __init__(self, *args, **kw):
130            super(ManifestSource, self).__init__(*args, **kw)
131            self.sample = (item, dict())
132
133    provideUtility(ManifestSource,
134        name=u'quintagroup.transmogrifier.tests.manifestsource')
135
136    from quintagroup.transmogrifier.manifest import ManifestSection
137    section = ManifestSection(test.globs['transmogrifier'], 
138        'manifest', {'blueprint': ''}, iter(()))
139    data = section.createManifest(item['_entries'])
140    test.globs['data'] = data
141
142def marshallSetUp(test):
143    sectionsSetUp(test)
144
145    from Products.Archetypes.interfaces import IBaseObject
146
147    class MockCriterion(object):
148        implements(IBaseObject)
149        _last_path = None
150        indexed = ()
151        def indexObject(self):
152            self.indexed += (self._last_path,)
153
154    class MockPortal(object):
155        implements(IBaseObject)
156
157        criterion = MockCriterion()
158
159        _last_path = None
160        def unrestrictedTraverse(self, path, default):
161            if path[0] == '/':
162                return default # path is absolute
163            if isinstance(path, unicode):
164                return default
165            if path == 'not/existing/bar':
166                return default
167            if path == 'topic/criterion':
168                self._last_path = path
169                self.criterion._last_path = path
170                return self.criterion
171            if path.endswith('/notatcontent'):
172                return object()
173            self._last_path = path
174            return self
175
176        def getId(self):
177            return "plone"
178
179        indexed = ()
180        def indexObject(self):
181            self.indexed += (self._last_path,)
182
183        marshalled = ()
184        def marshall(self, instance, **kwargs):
185            self.marshalled += ((self._last_path, kwargs.get('atns_exclude')),)
186            # Marshall often fails to export topic criteria
187            if isinstance(instance, MockCriterion):
188                return None, None, None
189            else:
190                return None, None, "marshalled"
191
192        demarshalled = ()
193        def demarshall(self, instance, data):
194            # we don't need to test Marshall product, only check if we call it's components
195            self.demarshalled += (self._last_path,)
196
197    portal = MockPortal()
198    test.globs['plone'] = portal
199    test.globs['transmogrifier'].context = test.globs['plone']
200
201    from Products.Marshall import registry
202    def getComponent(name):
203        return portal
204    registry.getComponent = getComponent
205
206    class MarshallSource(SampleSource):
207        classProvides(ISectionBlueprint)
208        implements(ISection)
209
210        def __init__(self, *args, **kw):
211            super(MarshallSource, self).__init__(*args, **kw)
212            self.sample = (
213                dict(),
214                dict(_path='spam/eggs/foo', _excluded_fields=('file', 'image')),
215                dict(_path='topic/criterion'),
216                dict(_path='not/existing/bar'),
217                dict(_path='spam/eggs/notatcontent', 
218                     _files=dict(marshall=dict(data='xml', name='.marshall.xml'))),
219            )
220    provideUtility(MarshallSource,
221        name=u'quintagroup.transmogrifier.tests.marshallsource')
222
223def propertyManagerSetUp(test):
224    sectionsSetUp(test)
225
226    from OFS.interfaces import IPropertyManager
227
228    class MockPortal(object):
229        implements(IPropertyManager)
230
231        _properties = (
232            {'id':'title', 'type': 'string', 'mode': 'w'},
233            {'id':'description', 'type': 'string', 'mode': 'w'},
234            {'id':'encoding', 'type': 'string', 'mode': 'w'},
235            {'id':'author', 'type': 'string', 'mode': 'w'}
236        )
237
238        _last_path = None
239        def unrestrictedTraverse(self, path, default):
240            if path[0] == '/':
241                return default # path is absolute
242            if isinstance(path, unicode):
243                return default
244            if path == 'not/existing/bar':
245                return default
246            if path.endswith('/notatcontent'):
247                return object()
248            self._last_path = path
249            return self
250
251        def _propertyMap(self):
252            return self._properties
253
254        def getProperty(self, id, d=None):
255            return 'value'
256
257        def propdict(self):
258            d={}
259            for p in self._properties:
260                d[p['id']]=p
261            return d
262
263        updated = ()
264        def _updateProperty(self, id, value):
265            self.updated += ((self._last_path, id, value.strip()))
266
267    portal = MockPortal()
268    test.globs['plone'] = portal
269    test.globs['transmogrifier'].context = test.globs['plone']
270
271    class PropertyManagerSource(SampleSource):
272        classProvides(ISectionBlueprint)
273        implements(ISection)
274
275        def __init__(self, *args, **kw):
276            super(PropertyManagerSource, self).__init__(*args, **kw)
277            self.sample = (
278                dict(),
279                dict(_path='not/existing/bar'),
280                dict(_path='spam/eggs/notatcontent'),
281                dict(_path='spam/eggs/foo', _excluded_properties=('encoding',)),
282            )
283
284    provideUtility(PropertyManagerSource,
285        name=u'quintagroup.transmogrifier.tests.propertymanagersource')
286
287def commentsSetUp(test):
288    sectionsSetUp(test)
289
290    class MockDiscussionItem(object):
291        creator = 'creator'
292        modified = 'date'
293
294        def __init__(self, reply, text=""):
295            self.in_reply_to = reply
296            self.text = text
297
298        def __of__(self, container):
299            return self
300
301        def getMetadataHeaders(self):
302            return []
303
304        def setMetadata(self, headers):
305            pass
306
307        def Creator(self):
308            return self.creator
309
310        def addCreator(self, creator):
311            self.creator = creator
312
313        def ModificationDate(self):
314            return self.modified
315
316        def setModificationDate(self, date):
317            self.modified = date
318
319        def setFormat(self, format):
320            pass
321
322        def _edit(self, text=None):
323            self.text = text
324
325        def indexObject(self):
326            pass
327
328        def __repr__(self):
329            return "<DicussionItem %s %s %s %s>" % (
330                self.Creator(),
331                self.ModificationDate(),
332                self.in_reply_to,
333                self.text
334                )
335
336    from Products.CMFDefault import DiscussionItem
337    DiscussionItem.DiscussionItem = MockDiscussionItem
338
339    class MockPortal(object):
340        _discussion = {
341            '1': MockDiscussionItem(None, 'comment to content'),
342            '2': MockDiscussionItem('1', 'reply to first comment'),
343            '3': MockDiscussionItem(None, 'other comment to content')
344        }
345        _container = {}
346
347        @property
348        def talkback(self):
349            return self
350
351        def objectItems(self):
352            l = self._discussion.items()
353            l.sort(key=lambda x: int(x[0]))
354            return l
355
356        def unrestrictedTraverse(self, path, default):
357            if path[0] == '/':
358                return default # path is absolute
359            if isinstance(path, unicode):
360                return default
361            if path == 'not/existing/bar':
362                return default
363            if path.endswith('/notdiscussable'):
364                return object()
365            return self
366
367        def getDiscussionFor(self, obj):
368            return self
369
370    portal = MockPortal()
371    test.globs['plone'] = portal
372    test.globs['transmogrifier'].context = test.globs['plone']
373
374    class CommentsSource(SampleSource):
375        classProvides(ISectionBlueprint)
376        implements(ISection)
377
378        def __init__(self, *args, **kw):
379            super(CommentsSource, self).__init__(*args, **kw)
380            self.sample = (
381                dict(),
382                dict(_path='not/existing/bar'),
383                dict(_path='spam/eggs/notdiscussable'),
384                dict(_path='spam/eggs/foo'),
385            )
386
387    provideUtility(CommentsSource,
388        name=u'quintagroup.transmogrifier.tests.commentssource')
389
390
391def dataCorrectorSetUp(test):
392    sectionsSetUp(test)
393
394    class MockPortal(object):
395        def unrestrictedTraverse(self, path, default):
396            if path[0] == '/':
397                return default # path is absolute
398            if isinstance(path, unicode):
399                return default
400            if path == 'not/existing/bar':
401                return default
402            if path.endswith('/notadaptable'):
403                return object()
404            return self
405
406    portal = MockPortal()
407    test.globs['plone'] = portal
408    test.globs['transmogrifier'].context = test.globs['plone']
409
410    from quintagroup.transmogrifier.interfaces import IExportDataCorrector, \
411        IImportDataCorrector
412
413    class MockExportAdapter(object):
414        implements(IExportDataCorrector)
415        adapts(MockPortal)
416        def __init__(self, context):
417            self.context = context
418
419        def __call__(self, data):
420            return "modified export data"
421
422    provideAdapter(MockExportAdapter, name="marshall")
423
424    class MockImportAdapter(object):
425        implements(IImportDataCorrector)
426        adapts(MockPortal)
427        def __init__(self, context):
428            self.context = context
429
430        def __call__(self, data):
431            return "modified import data"
432
433    provideAdapter(MockImportAdapter, name="manifest")
434
435    class DataCorrectorSource(SampleSource):
436        classProvides(ISectionBlueprint)
437        implements(ISection)
438
439        def __init__(self, *args, **kw):
440            super(DataCorrectorSource, self).__init__(*args, **kw)
441            self.sample = (
442                dict(),
443                dict(_files=dict(marshall="item hasn't path")),
444                dict(_path='spam/eggs/foo'),
445                dict(_path='not/existing/bar'),
446                dict(_path='spam/eggs/notadaptable', _files=dict(marshall="object isn't adaptable")),
447                dict(_path='spam/eggs/foo',
448                     _files=dict(marshall='marshall data', unchanged='this must be unchanged')),
449                dict(_path='spam/eggs/foo',
450                     _files=dict(manifest='manifest data', unchanged='this must be unchanged')),
451            )
452
453    provideUtility(DataCorrectorSource,
454        name=u'quintagroup.transmogrifier.tests.datacorrectorsource')
455
456def writerSetUp(test):
457    sectionsSetUp(test)
458
459    class MockExportContext(object):
460        def __init__( self, *args, **kwargs):
461            self.args = args
462            for k, v in kwargs.items():
463                setattr(self, k, v)
464            self._wrote = []
465
466        def __getitem__(self, name):
467            return getattr(self, name, None)
468
469        def __contains__(self, name):
470            return hasattr(self, name)
471
472        def writeDataFile(self, filename, text, content_type, subdir=None):
473            filename = '%s/%s' % (subdir, filename)
474            self._wrote.append((filename, text, content_type))
475
476        def __repr__(self):
477            s = " ".join(["%s=%s" % (k,v) for k,v in self.__dict__.items()])
478            return "<%s %s>" % (self.__class__.__name__, s)
479
480
481    from Products.GenericSetup import context
482
483    context.DirectoryExportContext = type('Directory', (MockExportContext,), {})
484    context.TarballExportContext = type('Tarball', (MockExportContext,), {})
485    context.SnapshotExportContext = type('Snapshot', (MockExportContext,), {})
486
487    class WriterSource(SampleSource):
488        classProvides(ISectionBlueprint)
489        implements(ISection)
490
491        def __init__(self, *args, **kw):
492            super(WriterSource, self).__init__(*args, **kw)
493            self.sample = (
494                dict(_path='spam/eggs/foo'),
495                dict(_files=dict(mock=dict(name='.first.xml', data='some data'))),
496                dict(_path='spam/eggs/foo',
497                     _files=dict(mock=dict(name='.first.xml', data='some data'),
498                                 other=dict(name='.second.xml', data='other data'))),
499                dict(_path='other/path',
500                     _files=dict(mock=dict(name='.third.xml', data='some data')))
501            )
502
503    provideUtility(WriterSource,
504        name=u'quintagroup.transmogrifier.tests.writersource')
505
506    class SingleItemSource(SampleSource):
507        classProvides(ISectionBlueprint)
508        implements(ISection)
509
510        def __init__(self, *args, **kw):
511            super(SingleItemSource, self).__init__(*args, **kw)
512            self.sample = (
513                dict(_path='', _files={}),
514            )
515
516    provideUtility(SingleItemSource,
517        name=u"quintagroup.transmogrifier.tests.singleitemsource")
518
519def readerSetUp(test):
520    sectionsSetUp(test)
521
522    class MockImportContext(object):
523
524        _dirs = [
525            'structure',
526            'structure/news', 'structure/news/recent',
527            'structure/pages', 'structure/pages/front-page',
528        ]
529        _files = [
530            'structure/.properties.xml',
531            'structure/other.file',
532            'structure/news/.objects.xml',
533            'structure/pages/.objects.xml',
534            'structure/pages/front-page/.marshall.xml',
535            'structure/pages/front-page/.comments.xml',
536        ]
537
538        def __init__( self, *args, **kwargs):
539            self.args = args
540            for k, v in kwargs.items():
541                setattr(self, k, v)
542
543        def __repr__(self):
544            s = " ".join(["%s=%s" % (k,v) for k,v in self.__dict__.items()])
545            return "<%s %s>" % (self.__class__.__name__, s)
546
547        def readDataFile(self, filename, subdir=None):
548            return 'some data'
549
550        def isDirectory(self, path):
551            return path == '' or path in self._dirs
552
553        def listDirectory(self, path):
554            all_names = self._dirs + self._files
555            if path:
556                pfx_len = len(path)+1
557            else:
558                pfx_len = 0
559            names = []
560            for name in all_names:
561                if name == path:
562                    continue
563                if not name.startswith(path):
564                    continue
565                name = name[pfx_len:]
566                if '/' in name:
567                    continue
568                names.append(name)
569            return names
570
571    from Products.GenericSetup import context
572
573    context.DirectoryImportContext = type('Directory', (MockImportContext,),
574        {'listDirectory': lambda self, path: []})
575    context.TarballImportContext = type('Tarball', (MockImportContext,), {})
576    context.SnapshotImportContext = type('Snapshot', (MockImportContext,),
577        {'listDirectory': lambda self, path: []})
578
579def manifestImportSetUp(test):
580    sectionsSetUp(test)
581
582    man1 = """<?xml version="1.0" ?>
583<manifest>
584  <record type="Document">document1</record>
585  <record type="Folder">folder1</record>
586</manifest>
587"""
588
589    man2 = """<?xml version="1.0" ?>
590<manifest>
591  <record type="Document">document2</record>
592  <record type="Document">document3</record>
593</manifest>
594"""
595
596    item1 = dict(
597        _path='',
598        _files=dict(
599            manifest=dict(
600                name='.objects.xml',
601                data=man1
602            )
603        )
604    )
605
606    item2 = dict(
607        _path='document1',
608    )
609
610    item3 = dict(
611        _path='folder1',
612        _files=dict(
613            manifest=dict(
614                name='.objects.xml',
615                data=man2
616            )
617        )
618    )
619
620    item4 = dict(
621        _path='folder1/document2',
622    )
623
624    item5 = dict(
625        _path='document4',
626    )
627
628    class ManifestSource(SampleSource):
629        classProvides(ISectionBlueprint)
630        implements(ISection)
631
632        def __init__(self, *args, **kw):
633            super(ManifestSource, self).__init__(*args, **kw)
634            self.sample = (item1, dict(), item2, item3, item4, item5)
635
636    provideUtility(ManifestSource,
637        name=u'quintagroup.transmogrifier.tests.manifestsource')
638
639def substitutionSetUp(test):
640    sectionsSetUp(test)
641
642    class SubstitutionSource(SampleSource):
643        classProvides(ISectionBlueprint)
644        implements(ISection)
645
646        def __init__(self, *args, **kw):
647            super(SubstitutionSource, self).__init__(*args, **kw)
648            self.sample = (
649                {},
650                {'_type': 'Blog'},
651                {'_type': 'PloneFormMailer'},
652                {'_type': 'Document'},
653            )
654
655    provideUtility(SubstitutionSource,
656        name=u'quintagroup.transmogrifier.tests.substitutionsource')
657
658class MetaDirectivesTests(unittest.TestCase):
659    def setUp(self):
660        zcml.load_config('meta.zcml', quintagroup.transmogrifier)
661
662    def tearDown(self):
663        stylesheet_registry.clear()
664        cleanup.cleanUp()
665
666    def testEmptyZCML(self):
667        zcml.load_string('''\
668<configure xmlns:transmogrifier="http://namespaces.plone.org/transmogrifier">
669</configure>''')
670        self.assertEqual(stylesheet_registry.listStylesheetNames(), ())
671
672    def testConfigZCML(self):
673        zcml.load_string('''\
674<configure
675    xmlns:transmogrifier="http://namespaces.plone.org/transmogrifier">
676<transmogrifier:stylesheet
677    source="marshall"
678    from="Blog"
679    to="Weblog"
680    file="blog.xsl"
681    />
682</configure>''')
683        self.assertEqual(stylesheet_registry.listStylesheetNames(),
684                         (u'marshall:Blog:Weblog',))
685        path = os.path.split(quintagroup.transmogrifier.__file__)[0]
686        self.assertEqual(
687            stylesheet_registry.getStylesheet('marshall', 'Blog', 'Weblog'),
688            dict(from_=u'Blog',
689                 to=u'Weblog',
690                 file=os.path.join(path, 'blog.xsl'))
691        )
692
693    def testMultipleZCML(self):
694        zcml.load_string('''\
695<configure
696    xmlns:transmogrifier="http://namespaces.plone.org/transmogrifier">
697<transmogrifier:stylesheet
698    source="marshall"
699    from="Blog"
700    to="Weblog"
701    file="blog.xsl"
702    />
703<transmogrifier:stylesheet
704    source="propertymanager"
705    from="BlogEntry"
706    to="WeblogEntry"
707    file="blogentry.xsl"
708    />
709</configure>''')
710        self.assertEqual(stylesheet_registry.listStylesheetNames(),
711                         (u'marshall:Blog:Weblog', u'propertymanager:BlogEntry:WeblogEntry'))
712
713def xsltSetUp(test):
714    sectionsSetUp(test)
715
716    class XSLTSource(SampleSource):
717        classProvides(ISectionBlueprint)
718        implements(ISection)
719
720        def __init__(self, *args, **kw):
721            super(XSLTSource, self).__init__(*args, **kw)
722            self.sample = (
723                {},
724                {'_type': 'Weblog'},
725                {'_old_type': 'Blog'},
726                {'_old_type': 'Blog',
727                 '_type': 'Weblog',
728                 '_files': {'manifest': {'data': 'xml', 'name': 'manifest.xml'}}},
729                {'_old_type': 'Blog',
730                 '_type': 'Weblog',
731                 '_files': {'marshall': {'data': 'xml', 'name': 'marshall.xml'}}},
732            )
733
734    provideUtility(XSLTSource,
735        name=u'quintagroup.transmogrifier.tests.xsltsource')
736
737    from quintagroup.transmogrifier.xslt import XSLTSection, stylesheet_registry
738
739    XSLTSection.applyTransformations = lambda self, xml, xslt: 'transformed xml'
740    test.globs['stylesheet_registry'] = stylesheet_registry
741
742def test_suite():
743    import sys
744    suite = unittest.findTestCases(sys.modules[__name__])
745    suite.addTests((
746        doctest.DocFileSuite(
747            'sitewalker.txt',
748            setUp=siteWalkerSetUp, tearDown=tearDown),
749        doctest.DocFileSuite(
750            'manifest.txt',
751            setUp=manifestSetUp, tearDown=tearDown),
752        doctest.DocFileSuite(
753            'marshall.txt',
754            setUp=marshallSetUp, tearDown=tearDown),
755        doctest.DocFileSuite(
756            'propertymanager.txt',
757            setUp=propertyManagerSetUp, tearDown=tearDown),
758        doctest.DocFileSuite(
759            'discussioncontainer.txt',
760            setUp=commentsSetUp, tearDown=tearDown),
761        doctest.DocFileSuite(
762            'datacorrector.txt',
763            setUp=dataCorrectorSetUp, tearDown=tearDown),
764        doctest.DocFileSuite(
765            'writer.txt',
766            setUp=writerSetUp, tearDown=tearDown),
767        doctest.DocFileSuite(
768            'reader.txt',
769            setUp=readerSetUp, tearDown=tearDown),
770        doctest.DocFileSuite(
771            'manifest_import.txt',
772            setUp=manifestImportSetUp, tearDown=tearDown),
773        doctest.DocFileSuite(
774            'substitution.txt',
775            setUp=substitutionSetUp, tearDown=tearDown),
776        doctest.DocFileSuite(
777            'xslt.txt',
778            setUp=xsltSetUp, tearDown=tearDown),
779    ))
780    return suite
Note: See TracBrowser for help on using the repository browser.