##############################################################################
#
# Copyright (c) 2004 Zope Foundation 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.
#
##############################################################################
"""
zc.resourcelibrary tests
"""
import re
import unittest
import doctest
from io import BytesIO

from zope.configuration import xmlconfig
from zope.pagetemplate import pagetemplate

from zope.testbrowser.wsgi import TestBrowserLayer
from zope.app.wsgi.testlayer import BrowserLayer
from zope.testing import renormalizing

import zope.component.hooks
import zope.interface
import zope.publisher.interfaces.browser
import zope.security.management

import zc.resourcelibrary
from zc.resourcelibrary import resourcelibrary
from zc.resourcelibrary import publication
from zc.resourcelibrary import tal



@zope.interface.implementer(zope.publisher.interfaces.browser.IBrowserPublisher)
class TestFactory(object):

    def __init__(self, source, checker, name):
        self.name = name
        self.__Security_checker__ = checker

    def __call__(self, request):
        return self

    def __getitem__(self, name):
        return lambda: "http://localhost/@@/%s/%s" % (self.name, name)

    def publishTraverse(self, request, name):
        return getattr(self, name.replace('.', '_'))

    def foo_js(self):
        return 'foo = 1;\n'



#### testing framework ####

def zcml(s, execute=True, clear=(), site=None):
    zope.component.hooks.setSite(site)
    for i in clear:
        del resourcelibrary.library_info[i]
    xmlconfig.string(s, execute=execute)


class TestPageTemplate(pagetemplate.PageTemplate):
    def __init__(self, view):
        self.view = view
        super(TestPageTemplate, self).__init__()

    def pt_getContext(self, *args, **kws):
        context = super(TestPageTemplate, self).pt_getContext(*args, **kws)
        context['view'] = self.view
        return context


def zpt(s, view=None, content_type=None):
    request = publication.Request(body_instream=BytesIO(b''), environ={})
    zope.security.management.newInteraction(request)

    # if no set has been set, try setting it the view context
    if zope.component.hooks.getSite() is None and hasattr(view, 'context'):
        zope.component.hooks.setSite(view.context)

    pt = TestPageTemplate(view)

    # if the resource library expression hasn't been registered, do so
    engine = pt.pt_getEngine()
    type_name = 'resource_library'
    if type_name not in engine.types:
        engine.registerType(type_name, tal.ResourceLibraryExpression)

    pt.write(s)
    html = pt()
    zope.security.management.endInteraction()

    if content_type:
        request.response.setHeader("Content-Type", content_type)

    if html:
        request.response.setResult(html)
        body = request.response.consumeBody()
        return body if isinstance(body, str) else body.decode('utf-8')

#### tests ####

class TestResponse(unittest.TestCase):

    def test_empty_body(self):
        # If a response body is not html, guess that it is text/plain.
        # This follows the behavior of zope.publication's trunk as of
        # this writing.
        response = publication.Response()
        response.setResult('')
        # On Python 3, this has a ';charset=utf-8' parameter, but not
        # on Python 2.
        self.assertTrue(response.getHeader('content-type').startswith('text/plain'))

#### test setup ####

class _ResourceLibraryFunctionalLayer(TestBrowserLayer,
                                      BrowserLayer):
    def __init__(self):
        super(_ResourceLibraryFunctionalLayer, self).__init__(zc.resourcelibrary)

ResourceLibraryFunctionalLayer = _ResourceLibraryFunctionalLayer()

# Note that renormalizing.IGNORE_EXCEPTION_MODULE_IN_PYTHON2 in
# zope.testing 4.6.1 fails with multi-line exception results like
# those generated by zope.configuration, (and you can't start a traceback
# line with ellipsis, so we have to normalize.)

checker = renormalizing.RENormalizing((
    (re.compile(r"u'([^']*)'"), r"'\1'"),
    (re.compile(r'zope.configuration.xmlconfig.ZopeXMLConfigurationError'),
     'ZopeXMLConfigurationError'),
    (re.compile(r'zope.configuration.exceptions.ConfigurationError'),
     'ConfigurationError'),
    (re.compile(r'zope.configuration.config.ConfigurationExecutionError'),
     'ConfigurationExecutionError'),
))


def test_suite():

    suite = doctest.DocFileSuite(
        '../README.rst',
        'duplicate_declarations.rst',
        'localsite.rst',
        globs={
            'zcml': zcml,
            'zpt': zpt,
            'getRootFolder': ResourceLibraryFunctionalLayer.getRootFolder,
        },
        optionflags=(doctest.NORMALIZE_WHITESPACE
                     | doctest.ELLIPSIS
                     | renormalizing.IGNORE_EXCEPTION_MODULE_IN_PYTHON2),
        checker=checker,
        )
    suite.layer = ResourceLibraryFunctionalLayer
    return unittest.TestSuite((
        suite,
        unittest.defaultTestLoader.loadTestsFromName(__name__),
    ))

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')
