##############################################################################
#
# Copyright (c) 2001, 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.
#
##############################################################################
"""API tests

$Id: test_api.py 27165 2004-08-17 11:20:31Z hdima $
"""
import unittest

from zope import component
from zope.component import servicenames
from zope.component import getAdapter, queryAdapter
from zope.component import getAdapterInContext, queryAdapterInContext
from zope.component import getService
from zope.component import getUtility, queryUtility
from zope.component import getDefaultViewName
from zope.component import queryMultiAdapter
from zope.component.service import serviceManager
from zope.component.exceptions import ComponentLookupError
from zope.component.servicenames import Adapters
from zope.component.tests.placelesssetup import PlacelessSetup
from zope.component.tests.request import Request
from zope.component.interfaces import IComponentArchitecture, IServiceService

from zope.interface import Interface, implements
from zope.interface.verify import verifyObject

class I1(Interface):
    pass
class I2(Interface):
    pass
class I3(Interface):
    pass

class Comp(object):
    implements(I2)
    def __init__(self, context, request=None): self.context = context

class Comp2(object):
    implements(I3)
    def __init__(self, context, request=None): self.context = context

comp = Comp(1)

class Ob(object):
    implements(I1)
    def __conform__(self, i):
        if i is IServiceService:
            return serviceManager

ob = Ob()

class Conforming(Ob):
    def __conform__(self, i):
        if i is I3:
            return Comp(self)
        else:
            return Ob.__conform__(self, i)

class StubServiceService(object):
    implements(IServiceService)  # This is a lie.

    def __init__(self):
        self.services = {}

    def setService(self, name, service):
        self.services[name] = service

    def getService(self, name):
        try:
            return self.services[name]
        except KeyError:
            raise ComponentLookupError, name


class ConformsToIServiceService(object):

    def __init__(self, serviceservice):
        self.serviceservice = serviceservice

    def __conform__(self, interface):
        if interface is IServiceService:
            return self.serviceservice


class Test(PlacelessSetup, unittest.TestCase):

    def testInterfaces(self):
        import zope.component
        self.failUnless(verifyObject(IComponentArchitecture, zope.component))


    def test_getGlobalServices(self):
        from zope.component import getGlobalServices
        from zope.component.service import IGlobalServiceManager

        gsm = getGlobalServices()
        self.assert_(IGlobalServiceManager.providedBy(gsm))
        self.assert_(getGlobalServices() is gsm)

    def test_getServices(self):
        from zope.component import getServices

        # We don't know anything about the default service manager, except
        # that it is an IServiceService.
        self.assert_(IServiceService.providedBy(getServices()))

        # Calling getServices with no args is equivalent to calling it
        # with a context of None.
        self.assert_(getServices() is getServices(None))

        # If the context passed to getServices is not None, it is
        # adapted to IServiceService and this adapter returned.
        # So, we create a context that can be adapted to IServiceService
        # using the __conform__ API.
        servicemanager = StubServiceService()
        context = ConformsToIServiceService(servicemanager)
        self.assert_(getServices(context) is servicemanager)

        # Using a context that is not adaptable to IServiceService should
        # fail.
        self.assertRaises(ComponentLookupError, getServices, object())

    def test_getService(self):
        from zope.component import getService, getServices

        # Getting the adapter service with no context given is the same
        # as getting the adapter service from the no-context service manager.
        self.assert_(getService(Adapters) is
                     getServices().getService(Adapters))
        # And, a context of 'None' is the same as not providing a context.
        self.assert_(getService(Adapters, None) is getService(Adapters))

        # If the context is adaptable to IServiceService then we use that
        # adapter.
        servicemanager = StubServiceService()
        adapterservice = object()
        servicemanager.setService(Adapters, adapterservice)
        context = ConformsToIServiceService(servicemanager)
        self.assert_(getService(Adapters, context) is adapterservice)

        # Using a context that is not adaptable to IServiceService should
        # fail.
        self.assertRaises(ComponentLookupError,
                          getService, Adapters, object())

    def testAdapterInContext(self):
        class I1(Interface):
            pass
        class I2(Interface):
            pass
        class C(object):
            implements(I1)
            def __conform__(self, iface, default=None):
                if iface == I2:
                    return 42
                
        ob = C()

        servicemanager = StubServiceService()
        context = ConformsToIServiceService(servicemanager)
        class I3(Interface):
            pass
        class StubAdapterService(object):
            def queryAdapter(self, ob, iface, name, default=None):
                if iface is I3:
                    return 43
                return default
        servicemanager.services[Adapters] = StubAdapterService()

        # If an object implements the interface you want to adapt to,
        # getAdapterInContext should simply return the object.
        self.assertEquals(getAdapterInContext(ob, I1, context), ob)
        self.assertEquals(queryAdapterInContext(ob, I1, context), ob)

        # If an object conforms to the interface you want to adapt to,
        # getAdapterInContext should simply return the conformed object.
        self.assertEquals(getAdapterInContext(ob, I2, context), 42)
        self.assertEquals(queryAdapterInContext(ob, I2, context), 42)

        class I4(Interface):
            pass
        # If an adapter isn't registered for the given object and interface,
        # and you provide no default, raise ComponentLookupError...
        self.assertRaises(ComponentLookupError,
                          getAdapterInContext, ob, I4, context)

        # ...otherwise, you get the default
        self.assertEquals(queryAdapterInContext(ob, I4, context, 44), 44)

        # If you ask for an adapter for which something's registered
        # you get the registered adapter
        self.assertEquals(getAdapterInContext(ob, I3, context), 43)
        self.assertEquals(queryAdapterInContext(ob, I3, context), 43)

    def testAdapter(self):
        # If an adapter isn't registered for the given object and interface,
        # and you provide no default, raise ComponentLookupError...
        self.assertRaises(ComponentLookupError, getAdapter, ob, I2, '')

        # ...otherwise, you get the default
        self.assertEquals(queryAdapter(ob, I2, '', Test), Test)

        getService(Adapters).register([I1], I2, '', Comp)
        c = getAdapter(ob, I2, '')
        self.assertEquals(c.__class__, Comp)
        self.assertEquals(c.context, ob)

    def testInterfaceCall(self):
        getService(Adapters).register([I1], I2, '', Comp)
        c = I2(ob)
        self.assertEquals(c.__class__, Comp)
        self.assertEquals(c.context, ob)

    def testNamedAdapter(self):
        self.testAdapter()

        # If an adapter isn't registered for the given object and interface,
        # and you provide no default, raise ComponentLookupError...
        self.assertRaises(ComponentLookupError, getAdapter, ob, I2, 'test')

        # ...otherwise, you get the default
        self.assertEquals(queryAdapter(ob, I2, 'test', Test), Test)

        class Comp2(Comp): pass

        getService(Adapters).register([I1], I2, 'test', Comp2)
        c = getAdapter(ob, I2, 'test')
        self.assertEquals(c.__class__, Comp2)
        self.assertEquals(c.context, ob)

    def testQueryMultiAdapter(self):
        # Adapting a combination of 2 objects to an interface
        class DoubleAdapter(object):
            implements(I3)
            def __init__(self, first, second):
                self.first = first
                self.second = second
        class Ob2(object):
            implements(I2)
        ob2 = Ob2()
        context = None
        getService(Adapters, context).register([I1, I2], I3, '', DoubleAdapter)
        c = queryMultiAdapter((ob, ob2), I3, context=context)
        self.assertEquals(c.__class__, DoubleAdapter)
        self.assertEquals(c.first, ob)
        self.assertEquals(c.second, ob2)

    def testAdapterForInterfaceNone(self):

        # providing an adapter for None says that your adapter can
        # adapt anything to I2.
        getService(Adapters).register([None], I2, '', Comp)
        c = I2(ob)
        self.assertEquals(c.__class__, Comp)
        self.assertEquals(c.context, ob)

    def testUtility(self):
        self.assertRaises(ComponentLookupError, getUtility, I1, context=ob)
        self.assertRaises(ComponentLookupError, getUtility, I2, context=ob)
        self.assertEquals(queryUtility(I2, default=Test, context=ob), Test)

        getService('Utilities').provideUtility(I2, comp)
        self.assertEquals(id(getUtility(I2, context=ob)), id(comp))

    def testNamedUtility(self):
        from zope.component import getUtility, queryUtility
        from zope.component import getService
        from zope.component.exceptions import ComponentLookupError

        self.testUtility()

        self.assertRaises(ComponentLookupError,
                          getUtility, I1, 'test', context=ob)
        self.assertRaises(ComponentLookupError,
                          getUtility, I2, 'test', context=ob)
        self.assertEquals(queryUtility(I2, 'test', Test, context=ob),
                          Test)

        getService('Utilities').provideUtility(I2, comp, 'test')
        self.assertEquals(id(getUtility(I2, 'test', ob)), id(comp))

    def test_getAllUtilitiesRegisteredFor(self):
        class I21(I2):
            pass
        class Comp21(Comp):
            implements(I21)
        
        compbob = Comp('bob')
        comp21 = Comp21('21')
        comp21bob = Comp21('21bob')
        
        getService('Utilities').provideUtility(I2, comp)
        getService('Utilities').provideUtility(I21, comp21)
        getService('Utilities').provideUtility(I2, compbob, 'bob')
        getService('Utilities').provideUtility(I21, comp21bob, 'bob')

        comps = [comp, compbob, comp21, comp21bob]
        comps.sort()

        uts = list(component.getUtilitiesFor(I2))
        uts.sort()
        self.assertEqual(uts, [('', comp), ('bob', compbob)])

        uts = list(component.getAllUtilitiesRegisteredFor(I2))
        uts.sort()
        self.assertEqual(uts, comps)        

    def testView(self):
        from zope.component import getView, queryView, getService
        from zope.component.exceptions import ComponentLookupError

        self.assertRaises(ComponentLookupError,
                          getView, ob, 'foo', Request(I1))
        self.assertRaises(ComponentLookupError,
                          getView, ob, 'foo', Request(I2))
        self.assertEquals(queryView(ob, 'foo', Request(I2), Test), Test)

        getService(servicenames.Presentation).provideView(
            I1, 'foo', I2, Comp)
        c = getView(ob, 'foo', Request(I2))
        self.assertEquals(c.__class__, Comp)
        self.assertEquals(c.context, ob)

        self.assertRaises(ComponentLookupError,
                          getView, ob, 'foo2', Request(I1))
        self.assertRaises(ComponentLookupError,
                          getView, ob, 'foo2', Request(I2))
        self.assertEquals(queryView(ob, 'foo2', Request(I2), Test), Test)

        self.assertEquals(queryView(ob, 'foo2', Request(I1), None), None)

    def testView_w_provided(self):
        from zope.component import getView, queryView, getService
        from zope.component.exceptions import ComponentLookupError

        self.assertRaises(ComponentLookupError,
                          getView, ob, 'foo', Request(I1), providing=I3)
        self.assertRaises(ComponentLookupError,
                          getView, ob, 'foo', Request(I2), providing=I3)
        self.assertEquals(
            queryView(ob, 'foo', Request(I2), Test, providing=I3),
            Test)

        getService(servicenames.Presentation).provideView(
            I1, 'foo', I2, Comp)

        self.assertRaises(ComponentLookupError,
                          getView, ob, 'foo', Request(I1), providing=I3)
        self.assertRaises(ComponentLookupError,
                          getView, ob, 'foo', Request(I2), providing=I3)
        self.assertEquals(
            queryView(ob, 'foo', Request(I2), Test, providing=I3),
            Test)


        getService(servicenames.Presentation).provideView(
            I1, 'foo', I2, Comp, providing=I3)

        c = getView(ob, 'foo', Request(I2), providing=I3)
        self.assertEquals(c.__class__, Comp)
        self.assertEquals(c.context, ob)

    def testMultiView(self):
        from zope.component import queryMultiView, getService
        from zope.component.exceptions import ComponentLookupError

        class Ob2(object):
            implements(I2)

        ob2 = Ob2()

        class IRequest(Interface):
            pass

        request = Request(IRequest)

        class MV(object):
            implements(I3)
            def __init__(self, context, other, request):
               self.context, self.other, self.request = context, other, request

        self.assertEquals(
            queryMultiView((ob, ob2), request, I3, 'foo', 42), 42)

        getService(servicenames.Presentation).provideAdapter(
            IRequest, MV, 'foo', (I1, I2), I3)

        view = queryMultiView((ob, ob2), request, I3, 'foo')
        self.assertEquals(view.__class__, MV)
        self.assertEquals(view.context, ob)
        self.assertEquals(view.other, ob2)
        self.assertEquals(view.request, request)

    def test_viewProvidingFunctions(self):        
        # Confirm that a call to getViewProving/queryViewProviding simply 
        # passes its arguments through to getView/queryView - here we hack
        # getView and queryView to inspect the args passed through.
        import zope.component

        # hack zope.component.getView
        def getView(object, name, request, context, providing):
            self.args = [object, name, request, context, providing]
        savedGetView = zope.component.getView
        zope.component.getView = getView

        # confirm pass through of args to getView by way of getViewProviding
        zope.component.getViewProviding(
            object='object', providing='providing', request='request', 
            context='context')
        self.assertEquals(self.args, 
            ['object', '', 'request', 'providing', 'context'])

        # hack zope.component.queryView
        def queryView(object, name, request, default, providing, context):
            self.args = [object, name, request, default, providing, context]
        savedQueryView = zope.component.queryView
        zope.component.queryView = queryView

        # confirm pass through of args to queryView by way of queryViewProviding 
        zope.component.queryViewProviding(
            object='object', providing='providing', request='request', 
            default='default', context='context')
        self.assertEquals(self.args, 
            ['object', '', 'request', 'default', 'providing', 'context'])

        # restore zope.component
        zope.component.getView = savedGetView
        zope.component.queryView = savedQueryView

    def testResource(self):
        from zope.component import getResource, queryResource, getService
        from zope.component.exceptions import ComponentLookupError

        r1 = Request(I1)
        r2 = Request(I2)

        self.assertRaises(ComponentLookupError, getResource, 'foo', r1)
        self.assertRaises(ComponentLookupError, getResource, 'foo', r2)
        self.assertEquals(queryResource('foo', r2, Test), Test)

        getService(servicenames.Presentation).provideResource(
            'foo', I2, Comp)
        c = getResource('foo', r2)
        self.assertEquals(c.__class__, Comp)
        self.assertEquals(c.context, r2)

        self.assertRaises(ComponentLookupError, getResource, 'foo2', r1, ob)
        self.assertRaises(ComponentLookupError, getResource, 'foo2', r2)
        self.assertEquals(queryResource('foo2', r2, Test, ob), Test)

        self.assertEquals(queryResource('foo2', r1, None), None)

    def testResource_w_provided(self):
        from zope.component import getResource, queryResource, getService
        from zope.component.exceptions import ComponentLookupError

        r1 = Request(I1)
        r2 = Request(I2)

        self.assertRaises(ComponentLookupError,
                          getResource, 'foo', r1, providing=I3)
        self.assertRaises(ComponentLookupError,
                          getResource, 'foo', r2, providing=I3)
        self.assertEquals(queryResource('foo', r2, Test, providing=I3),
                          Test)

        getService(servicenames.Presentation).provideResource(
            'foo', I2, Comp)

        self.assertRaises(ComponentLookupError,
                          getResource, 'foo', r1, providing=I3)
        self.assertRaises(ComponentLookupError,
                          getResource, 'foo', r2, providing=I3)
        self.assertEquals(queryResource('foo', r2, Test, providing=I3),
                          Test)


        getService(servicenames.Presentation).provideResource(
            'foo', I2, Comp, providing=I3)

        c = getResource('foo', r2, providing=I3)
        self.assertEquals(c.__class__, Comp)
        self.assertEquals(c.context, r2)

    def testViewWithContextArgument(self):
        # Basically the same as testView, but exercising the context
        # argument. As this only tests global views, the context
        # argument is pretty much a no-operation.
        from zope.component import getView, queryView, getService
        from zope.component.exceptions import ComponentLookupError

        self.assertRaises(ComponentLookupError,
                          getView, ob, 'foo', Request(I1), context=ob)
        self.assertRaises(ComponentLookupError,
                          getView, ob, 'foo', Request(I2), context=ob)
        self.assertEquals(queryView(ob, 'foo', Request(I2), Test, context=ob),
                          Test)

        getService(servicenames.Presentation).provideView(
            I1, 'foo', I2, Comp)
        c = getView(ob, 'foo', Request(I2), context=ob)
        self.assertEquals(c.__class__, Comp)
        self.assertEquals(c.context, ob)

        self.assertRaises(ComponentLookupError,
                          getView, ob, 'foo2', Request(I1), context=ob)
        self.assertRaises(ComponentLookupError,
                          getView, ob, 'foo2', Request(I2), context=ob)
        self.assertEquals(queryView(ob, 'foo2', Request(I2), Test,
                                    context=ob),
                          Test)

        self.assertEquals(queryView(ob, 'foo2', Request(I1), None,
                                    context=ob),
                          None)

    def testDefaultViewName(self):
        from zope.component import getService
        from zope.exceptions import NotFoundError
        viewService = getService(servicenames.Presentation)
        self.assertRaises(NotFoundError,
                          getDefaultViewName,
                          ob, Request(I1))
        viewService.setDefaultViewName(I1, I2, 'sample_name')
        self.assertEquals(getDefaultViewName(ob, Request(I2)),
                          'sample_name')
        self.assertRaises(NotFoundError,
                          getDefaultViewName,
                          ob, Request(I1))


class TestNoSetup(unittest.TestCase):

    def testNotBrokenWhenNoService(self):
        # Both of those things emit DeprecationWarnings.
        self.assertRaises(TypeError, I2, ob)
        self.assertEquals(I2(ob, 42), 42)
        pass

def test_suite():
    return unittest.TestSuite((
        unittest.makeSuite(Test),
        unittest.makeSuite(TestNoSetup),
        ))

if __name__ == "__main__":
    unittest.TextTestRunner().run(test_suite())
