##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
""" Unit tests for IOpaqueItems implementations.

$Id: test_OpaqueItems.py 77004 2007-06-24 08:57:54Z yuppie $
"""

import unittest
import Testing

from zope.interface import implements

from Products.CMFCore.interfaces import ICallableOpaqueItem
from Products.CMFCore.interfaces import ICallableOpaqueItemEvents
from Products.CMFCore.interfaces import IContentish
from Products.CMFCore.interfaces.IOpaqueItems \
        import ICallableOpaqueItem as z2ICallableOpaqueItem
from Products.CMFCore.interfaces.IOpaqueItems \
        import ICallableOpaqueItemEvents as z2ICallableOpaqueItemEvents
from Products.CMFCore.PortalFolder import PortalFolder
from Products.CMFCore.testing import TraversingEventZCMLLayer
from Products.CMFCore.tests.base.dummy \
    import DummyContent as OriginalDummyContent
from Products.CMFCore.tests.base.testcase import SecurityTest
from Products.CMFCore.TypesTool import TypesTool


# -------------------------------------------
# Helper classes and functions
# -------------------------------------------

def extra_meta_types():
    return [{'name': 'Dummy', 'action': 'manage_addFolder',
             'permission': 'View'}]

def addDummyContent(container, id, opaqueItem):
    container._setObject(id, DummyContent(id, opaqueItem=opaqueItem, catalog=1))
    return getattr(container, id)


class DummyContent(OriginalDummyContent):

    """ A Dummy piece of PortalContent with additional attributes
    """
    implements(IContentish)

    def __init__(self, id='dummy', opaqueItem=None, *args, **kw):
        OriginalDummyContent.__init__(self, id, *args, **kw)
        if opaqueItem is None:
            self.opaqueItem = 'noncallable'
            self.opaqueItemsId = 'opaqueItem'
        elif isinstance(opaqueItem, basestring):
            Hooks(self, opaqueItem)
            self.opaqueItemsId = opaqueItem
        else:
            opaqueItem(self, 'opaqueItem')
            self.opaqueItemsId = 'opaqueItem'

    # Ensure additional attributes get copied
    def _getCopy(self, container):
        obj = DummyContent(self.id, catalog=self.catalog)
        setattr(obj, self.opaqueItemsId, getattr(self, self.opaqueItemsId))
        return obj

    def isNotifiedByAfterAdd(self):
        return getattr(getattr(self, self.opaqueItemsId), 'addCount', None)

    def isNotifiedByAfterClone(self):
        return getattr(getattr(self, self.opaqueItemsId), 'cloneCount', None)

    def isNotifiedByBeforeDelete(self):
        return getattr(getattr(self, self.opaqueItemsId), 'deleteCount', None)


class OpaqueBase:
    """ Dummy opaque item without manage_after/before hookes
    """
    def __init__(self, obj, id):
        self.addCount = self.cloneCount = self.deleteCount = 0
        self.addCounter = self.cloneCounter = self.deleteCounter = 1
        self.id = id
        setattr(obj, id, self)

    def __call__():
        return

    def getId(self):
        return self.id


class Marker(OpaqueBase):

    """ Opaque item without manage_after/before hookes but marked as callable
    """

    implements(ICallableOpaqueItem)
    __implements__ = (
        z2ICallableOpaqueItem,
    )


class Hooks(OpaqueBase):

    """ Opaque item with manage_after/before hooks but not marked as callable
    """

    implements(ICallableOpaqueItemEvents)
    __implements__ = (
        z2ICallableOpaqueItemEvents,
    )

    def manage_afterAdd(self, item, container):
        self.addCount = self.addCounter
        self.addCounter += 1

    def manage_afterClone(self, item):
        self.cloneCount = self.cloneCounter
        self.cloneCounter += 1

    def manage_beforeDelete(self, item, container):
        self.deleteCount = self.deleteCounter
        self.deleteCounter += 1


class MarkerAndHooks(Marker, Hooks):

    """ Opaque item with manage_after/before hookes and marked as callable
    """
    __implements__ = Marker.__implements__ + Hooks.__implements__


# -------------------------------------------
# Unit Tests
# -------------------------------------------

class ManageBeforeAfterTests(SecurityTest):

    layer = TraversingEventZCMLLayer

    def setUp(self):
        SecurityTest.setUp(self)

        root = self.root

        # setting up types tool
        root._setObject( 'portal_types', TypesTool() )

        # setup portal
        try:
            root._delObject('test')
        except AttributeError:
            pass
        root._setObject('test', PortalFolder('test', ''))
        self.test = test = self.root.test

        # setting up folders
        test._setObject( 'folder', PortalFolder( 'folder', '' ) )
        folder = self.folder = test.folder
        folder._setObject( 'sub', PortalFolder( 'sub', '' ) )
        sub = self.sub = folder.sub

        #----- hacks to allow pasting (see also test_PortalFolder)
        # WAAA! force sub to allow paste of Dummy object.
        sub.all_meta_types = extra_meta_types()

        # delete items if necessary
        try:
            folder._delObject('dummy')
        except AttributeError:
            pass
        try:
            sub._delObject('dummy')
        except AttributeError:
            pass

    def test_nonCallableItem(self):
        # no exception should be raised
        folder = self.folder
        sub = self.sub
        dummy = addDummyContent(folder, 'dummy', None)

        # WAAAA! must get _p_jar set
        old, dummy._p_jar = sub._p_jar, self.root._p_jar
        try:
            cp = folder.manage_copyObjects(ids=['dummy'])
            sub.manage_pasteObjects(cp)
        finally:
            dummy._p_jar = old

    def test_callableItemWithMarkerOnly(self):
        folder = self.folder
        sub = self.sub
        dummy = addDummyContent(folder, 'dummy', Marker)

        self.failIf(dummy.isNotifiedByAfterAdd())
        self.failIf(dummy.isNotifiedByAfterClone())
        self.failIf(dummy.isNotifiedByBeforeDelete())

        # WAAAA! must get _p_jar set
        old, dummy._p_jar = sub._p_jar, self.root._p_jar
        try:
            cp = folder.manage_copyObjects(ids=['dummy'])
            sub.manage_pasteObjects(cp)
        finally:
            dummy._p_jar = old

        self.failIf(dummy.isNotifiedByAfterAdd())
        self.failIf(dummy.isNotifiedByAfterClone())
        self.failIf(dummy.isNotifiedByBeforeDelete())

    def test_callableItemWithHooksOnly(self):
        folder = self.folder
        sub = self.sub
        dummy = addDummyContent(folder, 'dummy', Hooks)

        self.failIf(dummy.isNotifiedByAfterAdd())
        self.failIf(dummy.isNotifiedByAfterClone())
        self.failIf(dummy.isNotifiedByBeforeDelete())

        # WAAAA! must get _p_jar set
        old, dummy._p_jar = sub._p_jar, self.root._p_jar
        try:
            cp = folder.manage_copyObjects(ids=['dummy'])
            sub.manage_pasteObjects(cp)
        finally:
            dummy._p_jar = old

        self.failIf(dummy.isNotifiedByAfterAdd())
        self.failIf(dummy.isNotifiedByAfterClone())
        self.failIf(dummy.isNotifiedByBeforeDelete())

    def test_callableItemWithMarkerAndHooks(self):
        folder = self.folder
        sub = self.sub
        dummy = addDummyContent(folder, 'dummy', MarkerAndHooks)

        self.assertEqual(dummy.isNotifiedByAfterAdd(), 1)
        self.failIf(dummy.isNotifiedByAfterClone())
        self.failIf(dummy.isNotifiedByBeforeDelete())

        # WAAAA! must get _p_jar set
        old, dummy._p_jar = sub._p_jar, self.root._p_jar
        try:
            cp = folder.manage_copyObjects(ids=['dummy'])
            sub.manage_pasteObjects(cp)
        finally:
            dummy._p_jar = old

        self.assertEqual(dummy.isNotifiedByAfterAdd(), 2)
        self.assertEqual(dummy.isNotifiedByAfterClone(), 1)
        self.failIf(dummy.isNotifiedByBeforeDelete())

    def test_talkbackItem(self):
        folder = self.folder
        sub = self.sub

        dummy = addDummyContent(folder, 'dummy', 'talkback')

        self.assertEqual(dummy.isNotifiedByAfterAdd(), 1)
        self.failIf(dummy.isNotifiedByAfterClone())
        self.failIf(dummy.isNotifiedByBeforeDelete())

        # WAAAA! must get _p_jar set
        old, dummy._p_jar = sub._p_jar, self.root._p_jar
        try:
            cp = folder.manage_copyObjects(ids=['dummy'])
            sub.manage_pasteObjects(cp)
        finally:
            dummy._p_jar = old

        self.assertEqual(dummy.isNotifiedByAfterAdd(), 2)
        self.assertEqual(dummy.isNotifiedByAfterClone(), 1)
        self.failIf(dummy.isNotifiedByBeforeDelete())


def test_suite():
    return unittest.TestSuite((
        unittest.makeSuite(ManageBeforeAfterTests),
        ))

if __name__ == '__main__':
    from Products.CMFCore.testing import run
    run(test_suite())
