##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Test checkers for standard types

This is a test of the assertions made in
zope.security.checkers._default_checkers.
"""
from zope.security.checker import ProxyFactory, NamesChecker
from zope.security.interfaces import ForbiddenAttribute

import sys

def check_forbidden_get(object, attr):
    try:
        return getattr(object, attr)
    except ForbiddenAttribute, e:
        return 'ForbiddenAttribute: %s' % e[0]


def check_forbidden_setitem(object, item, value):
    try:
        object[item] = value
    except ForbiddenAttribute, e:
        return 'ForbiddenAttribute: %s' % e[0]


def check_forbidden_delitem(object, item):
    try:
        del object[item]
    except ForbiddenAttribute, e:
        return 'ForbiddenAttribute: %s' % e[0]


def check_forbidden_call(callable, *args):
    try:
        return callable(*args)
    except ForbiddenAttribute, e:
        return 'ForbiddenAttribute: %s' % e[0]


def test_dict():
    """Test that we can do everything we expect to be able to do

    with proxied dicts.

    >>> d = ProxyFactory({'a': 1, 'b': 2})

    >>> check_forbidden_get(d, 'clear') # Verify that we are protected
    'ForbiddenAttribute: clear'
    >>> check_forbidden_setitem(d, 3, 4) # Verify that we are protected
    'ForbiddenAttribute: __setitem__'

    >>> d['a']
    1
    >>> len(d)
    2
    >>> list(d)
    ['a', 'b']
    >>> d.get('a')
    1
    >>> int(d.has_key('a'))
    1

    >>> c = d.copy()
    >>> check_forbidden_get(c, 'clear')
    'ForbiddenAttribute: clear'
    >>> int(str(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}"))
    1

    >>> int(`c` in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}"))
    1


    >>> def sorted(x):
    ...    x = list(x)
    ...    x.sort()
    ...    return x

    >>> sorted(d.keys())
    ['a', 'b']
    >>> sorted(d.values())
    [1, 2]
    >>> sorted(d.items())
    [('a', 1), ('b', 2)]

    >>> sorted(d.iterkeys())
    ['a', 'b']
    >>> sorted(d.itervalues())
    [1, 2]
    >>> sorted(d.iteritems())
    [('a', 1), ('b', 2)]

    Always available:

    >>> int(d < d)
    0
    >>> int(d > d)
    0
    >>> int(d <= d)
    1
    >>> int(d >= d)
    1
    >>> int(d == d)
    1
    >>> int(d != d)
    0
    >>> int(bool(d))
    1
    >>> int(d.__class__ == dict)
    1

    """

def test_list():
    """Test that we can do everything we expect to be able to do

    with proxied lists.

    >>> l = ProxyFactory([1, 2])
    >>> check_forbidden_delitem(l, 0)
    'ForbiddenAttribute: __delitem__'
    >>> check_forbidden_setitem(l, 0, 3)
    'ForbiddenAttribute: __setitem__'
    >>> l[0]
    1
    >>> l[0:1]
    [1]
    >>> check_forbidden_setitem(l[:1], 0, 2)
    'ForbiddenAttribute: __setitem__'
    >>> len(l)
    2
    >>> tuple(l)
    (1, 2)
    >>> int(1 in l)
    1
    >>> l.index(2)
    1
    >>> l.count(2)
    1
    >>> str(l)
    '[1, 2]'
    >>> `l`
    '[1, 2]'
    >>> l + l
    [1, 2, 1, 2]

    Always available:

    >>> int(l < l)
    0
    >>> int(l > l)
    0
    >>> int(l <= l)
    1
    >>> int(l >= l)
    1
    >>> int(l == l)
    1
    >>> int(l != l)
    0
    >>> int(bool(l))
    1
    >>> int(l.__class__ == list)
    1


    """

def test_tuple():
    """Test that we can do everything we expect to be able to do

    with proxied lists.

    >>> l = ProxyFactory((1, 2))
    >>> l[0]
    1
    >>> l[0:1]
    (1,)
    >>> len(l)
    2
    >>> list(l)
    [1, 2]
    >>> int(1 in l)
    1
    >>> str(l)
    '(1, 2)'
    >>> `l`
    '(1, 2)'
    >>> l + l
    (1, 2, 1, 2)

    Always available:

    >>> int(l < l)
    0
    >>> int(l > l)
    0
    >>> int(l <= l)
    1
    >>> int(l >= l)
    1
    >>> int(l == l)
    1
    >>> int(l != l)
    0
    >>> int(bool(l))
    1
    >>> int(l.__class__ == tuple)
    1

    """

def test_iter():
    """
    >>> list(ProxyFactory(iter([1, 2])))
    [1, 2]
    >>> list(ProxyFactory(iter((1, 2))))
    [1, 2]
    >>> list(ProxyFactory(iter({1:1, 2:2})))
    [1, 2]
    >>> def f():
    ...     for i in 1, 2:
    ...             yield i
    ...
    >>> list(ProxyFactory(f()))
    [1, 2]
    >>> list(ProxyFactory(f)())
    [1, 2]
    """

def test_new_class():
    """

    >>> class C(object):
    ...    x = 1
    >>> C = ProxyFactory(C)
    >>> check_forbidden_call(C)
    'ForbiddenAttribute: __call__'
    >>> check_forbidden_get(C, '__dict__')
    'ForbiddenAttribute: __dict__'
    >>> s = str(C)
    >>> s = `C`
    >>> int(C.__module__ == __name__)
    1
    >>> len(C.__bases__)
    1
    >>> len(C.__mro__)
    2

    Always available:

    >>> int(C < C)
    0
    >>> int(C > C)
    0
    >>> int(C <= C)
    1
    >>> int(C >= C)
    1
    >>> int(C == C)
    1
    >>> int(C != C)
    0
    >>> int(bool(C))
    1
    >>> int(C.__class__ == type)
    1

    """

def test_new_instance():
    """

    >>> class C(object):
    ...    x, y = 1, 2
    >>> c = ProxyFactory(C(), NamesChecker(['x']))
    >>> check_forbidden_get(c, 'y')
    'ForbiddenAttribute: y'
    >>> check_forbidden_get(c, 'z')
    'ForbiddenAttribute: z'
    >>> c.x
    1
    >>> int(c.__class__ == C)
    1

    Always available:

    >>> int(c < c)
    0
    >>> int(c > c)
    0
    >>> int(c <= c)
    1
    >>> int(c >= c)
    1
    >>> int(c == c)
    1
    >>> int(c != c)
    0
    >>> int(bool(c))
    1
    >>> int(c.__class__ == C)
    1

    """

def test_classic_class():
    """

    >>> class C:
    ...    x = 1
    >>> C = ProxyFactory(C)
    >>> check_forbidden_call(C)
    'ForbiddenAttribute: __call__'
    >>> check_forbidden_get(C, '__dict__')
    'ForbiddenAttribute: __dict__'
    >>> s = str(C)
    >>> s = `C`
    >>> int(C.__module__ == __name__)
    1
    >>> len(C.__bases__)
    0

    Always available:

    >>> int(C < C)
    0
    >>> int(C > C)
    0
    >>> int(C <= C)
    1
    >>> int(C >= C)
    1
    >>> int(C == C)
    1
    >>> int(C != C)
    0
    >>> int(bool(C))
    1

    """

def test_classic_instance():
    """

    >>> class C(object):
    ...    x, y = 1, 2
    >>> c = ProxyFactory(C(), NamesChecker(['x']))
    >>> check_forbidden_get(c, 'y')
    'ForbiddenAttribute: y'
    >>> check_forbidden_get(c, 'z')
    'ForbiddenAttribute: z'
    >>> c.x
    1
    >>> int(c.__class__ == C)
    1

    Always available:

    >>> int(c < c)
    0
    >>> int(c > c)
    0
    >>> int(c <= c)
    1
    >>> int(c >= c)
    1
    >>> int(c == c)
    1
    >>> int(c != c)
    0
    >>> int(bool(c))
    1
    >>> int(c.__class__ == C)
    1

    """

def test_rocks():
    """
    >>> int(type(ProxyFactory(  object()  )) is object)
    1
    >>> int(type(ProxyFactory(  1  )) is int)
    1
    >>> int(type(ProxyFactory(  1.0  )) is float)
    1
    >>> int(type(ProxyFactory(  1l  )) is long)
    1
    >>> int(type(ProxyFactory(  1j  )) is complex)
    1
    >>> int(type(ProxyFactory(  None  )) is type(None))
    1
    >>> int(type(ProxyFactory(  'xxx'  )) is str)
    1
    >>> int(type(ProxyFactory(  u'xxx'  )) is unicode)
    1
    >>> int(type(ProxyFactory(  True  )) is type(True))
    1

    >>> from datetime import timedelta, datetime, date, time, tzinfo
    >>> int(type(ProxyFactory(  timedelta(1)  )) is timedelta)
    1
    >>> int(type(ProxyFactory(  datetime(2000, 1, 1)  )) is datetime)
    1
    >>> int(type(ProxyFactory(  date(2000, 1, 1)  )) is date)
    1
    >>> int(type(ProxyFactory(  time()  )) is time)
    1
    >>> int(type(ProxyFactory(  tzinfo() )) is tzinfo)
    1

    >>> try:
    ...     from pytz import UTC
    ... except ImportError:  # pytz checker only if pytz is present.
    ...     UTC = None
    >>> int(UTC is None or type(ProxyFactory(  UTC )) is type(UTC))
    1
    """

def test_iter_of_sequences():
    """
    >>> class X(object):
    ...   d = 1, 2, 3
    ...   def __getitem__(self, i):
    ...      return self.d[i]
    ...
    >>> x = X()

    We can iterate over sequences

    >>> list(x)
    [1, 2, 3]
    >>> c = NamesChecker(['__getitem__'])
    >>> p = ProxyFactory(x, c)

    Even if they are proxied

    >>> list(p)
    [1, 2, 3]

    But if the class has an iter:

    >>> X.__iter__ = lambda self: iter(self.d)
    >>> list(x)
    [1, 2, 3]

    We shouldn't be able to iterate if we don't have an assertion:

    >>> check_forbidden_call(list, p)
    'ForbiddenAttribute: __iter__'
    """

def test_interfaces_and_declarations():
    """Test that we can still use interfaces though proxies

    >>> import zope.interface
    >>> class I(zope.interface.Interface):
    ...     pass
    >>> class IN(zope.interface.Interface):
    ...     pass
    >>> class II(zope.interface.Interface):
    ...     pass
    >>> class N(object):
    ...     zope.interface.implements(I)
    ...     zope.interface.classProvides(IN)
    >>> n = N()
    >>> zope.interface.directlyProvides(n, II)
    >>> from zope.security.checker import ProxyFactory
    >>> N = ProxyFactory(N)
    >>> n = ProxyFactory(n)
    >>> I.implementedBy(N)
    True
    >>> IN.providedBy(N)
    True
    >>> I.providedBy(n)
    True
    >>> II.providedBy(n)
    True
    """

if sys.version_info >= (2, 6):
    def test_ABCMeta():
        """
        Test that we work with the ABCMeta meta class

        >>> import abc
        >>> class MyABC:
        ...     __metaclass__ = abc.ABCMeta

        >>> class Foo(MyABC): pass

        >>> class Bar(Foo): pass

        >>> PBar = ProxyFactory(Bar)
        >>> [c.__name__ for c in PBar.__mro__]
        ['Bar', 'Foo', 'MyABC', 'object']

        >>> issubclass(PBar, Foo)
        True

        >>> issubclass(Bar, Foo)
        True

        >>> PBar = ProxyFactory(PBar)
        >>> check_forbidden_call(PBar)
        'ForbiddenAttribute: __call__'
        >>> check_forbidden_get(PBar, '__dict__')
        'ForbiddenAttribute: __dict__'
        >>> s = str(PBar)
        >>> s = `PBar`
        >>> int(PBar.__module__ == __name__)
        1
        >>> len(PBar.__bases__)
        1

        Always available:

        >>> int(PBar < PBar)
        0
        >>> int(PBar > PBar)
        0
        >>> int(PBar <= PBar)
        1
        >>> int(PBar >= PBar)
        1
        >>> int(PBar == PBar)
        1
        >>> int(PBar != PBar)
        0
        >>> int(bool(PBar))
        1
        >>> int(PBar.__class__ == abc.ABCMeta)
        1
        """


def test_suite():
    from doctest import DocTestSuite
    return DocTestSuite()
