import struct
import unittest

import gobject
from gobject import GObject, GType, GEnum, new, PARAM_READWRITE, \
     PARAM_CONSTRUCT, PARAM_READABLE, PARAM_WRITABLE, PARAM_CONSTRUCT_ONLY
from gobject.constants import \
     TYPE_INT, TYPE_UINT, TYPE_LONG, \
     TYPE_ULONG, TYPE_INT64, TYPE_UINT64
from gobject.constants import \
     G_MININT, G_MAXINT, G_MAXUINT, G_MINLONG, G_MAXLONG, \
     G_MAXULONG

class PropertyObject(GObject):
    normal = gobject.property(type=str)
    construct = gobject.property(
        type=str,
        flags=PARAM_READWRITE|PARAM_CONSTRUCT, default='default')
    construct_only = gobject.property(
        type=str,
        flags=PARAM_READWRITE|PARAM_CONSTRUCT_ONLY)
    uint64 = gobject.property(
        type=TYPE_UINT64, flags=PARAM_READWRITE|PARAM_CONSTRUCT)

class TestProperties(unittest.TestCase):
    def testGetSet(self):
        obj = PropertyObject()
        obj.props.normal = "value"
        self.assertEqual(obj.props.normal, "value")

    def testListWithInstance(self):
        obj = PropertyObject()
        self.failUnless(hasattr(obj.props, "normal"))

    def testListWithoutInstance(self):
        self.failUnless(hasattr(PropertyObject.props, "normal"))

    def testSetNoInstance(self):
        def set(obj):
            obj.props.normal = "foobar"

        self.assertRaises(TypeError, set, PropertyObject)

    def testIterator(self):
        for obj in (PropertyObject.props, PropertyObject().props):
            for pspec in obj:
                gtype = GType(pspec)
                self.assertEqual(gtype.parent.name, 'GParam')
                self.failUnless(pspec.name in ['normal',
                                               'construct',
                                               'construct-only',
                                               'uint64'])
            self.assertEqual(len(obj), 4)

    def testNormal(self):
        obj = new(PropertyObject, normal="123")
        self.assertEqual(obj.props.normal, "123")
        obj.set_property('normal', '456')
        self.assertEqual(obj.props.normal, "456")
        obj.props.normal = '789'
        self.assertEqual(obj.props.normal, "789")

    def testConstruct(self):
        obj = new(PropertyObject, construct="123")
        self.assertEqual(obj.props.construct, "123")
        obj.set_property('construct', '456')
        self.assertEqual(obj.props.construct, "456")
        obj.props.construct = '789'
        self.assertEqual(obj.props.construct, "789")

    def testConstructOnly(self):
        obj = new(PropertyObject, construct_only="123")
        self.assertEqual(obj.props.construct_only, "123")
        self.assertRaises(TypeError,
                          setattr, obj.props, 'construct_only', '456')
        self.assertRaises(TypeError,
                          obj.set_property, 'construct-only', '456')

    def testUint64(self):
        obj = new(PropertyObject)
        self.assertEqual(obj.props.uint64, 0)
        obj.props.uint64 = 1L
        self.assertEqual(obj.props.uint64, 1L)
        obj.props.uint64 = 1
        self.assertEqual(obj.props.uint64, 1L)

    def testUInt64DefaultValue(self):
        try:
            class TimeControl(GObject):
                __gproperties__ = {
                    'time': (TYPE_UINT64, 'Time', 'Time',
                             0L, (1<<64) - 1, 0L,
                             PARAM_READABLE)
                    }
        except OverflowError, ex:
            self.fail(str(ex))

    def testRange(self):
        # kiwi code
        def max(c):
            return 2 ** ((8 * struct.calcsize(c)) - 1) - 1
        def umax(c):
            return 2 ** (8 * struct.calcsize(c)) - 1

        maxint = max('i')
        minint = -maxint - 1
        maxuint = umax('I')
        maxlong = max('l')
        minlong = -maxlong - 1
        maxulong = umax('L')
        maxint64 = max('q')
        minint64 = -maxint64 - 1
        maxuint64 = umax('Q')

        types = dict(int=(TYPE_INT, minint, maxint),
                     uint=(TYPE_UINT, 0, maxuint),
                     long=(TYPE_LONG, minlong, maxlong),
                     ulong=(TYPE_ULONG, 0, maxulong),
                     int64=(TYPE_INT64, minint64, maxint64),
                     uint64=(TYPE_UINT64, 0, maxuint64))

        def build_gproperties(types):
            d = {}
            for key, (gtype, min, max) in types.items():
                d[key] = (gtype, 'blurb', 'desc', min, max, 0,
                          PARAM_READABLE | PARAM_WRITABLE)
            return d

        class RangeCheck(GObject):
            __gproperties__ = build_gproperties(types)

            def __init__(self):
                self.values = {}
                GObject.__init__(self)

            def do_set_property(self, pspec, value):
                self.values[pspec.name] = value

            def do_get_property(self, pspec):
                return self.values.get(pspec.name, pspec.default_value)

        self.assertEqual(RangeCheck.props.int.minimum, minint)
        self.assertEqual(RangeCheck.props.int.maximum, maxint)
        self.assertEqual(RangeCheck.props.uint.minimum, 0)
        self.assertEqual(RangeCheck.props.uint.maximum, maxuint)
        self.assertEqual(RangeCheck.props.long.minimum, minlong)
        self.assertEqual(RangeCheck.props.long.maximum, maxlong)
        self.assertEqual(RangeCheck.props.ulong.minimum, 0)
        self.assertEqual(RangeCheck.props.ulong.maximum, maxulong)
        self.assertEqual(RangeCheck.props.int64.minimum, minint64)
        self.assertEqual(RangeCheck.props.int64.maximum, maxint64)
        self.assertEqual(RangeCheck.props.uint64.minimum, 0)
        self.assertEqual(RangeCheck.props.uint64.maximum, maxuint64)

        obj = RangeCheck()
        for key, (gtype, min, max) in types.items():
            self.assertEqual(obj.get_property(key),
                             getattr(RangeCheck.props, key).default_value)

            obj.set_property(key, min)
            self.assertEqual(obj.get_property(key), min)

            obj.set_property(key, max)
            self.assertEqual(obj.get_property(key), max)


    def testMulti(self):
        obj = PropertyObject()
        obj.set_properties(normal="foo",
                           uint64=7)
        normal, uint64 = obj.get_properties("normal", "uint64")
        self.assertEqual(normal, "foo")
        self.assertEqual(uint64, 7)

class TestProperty(unittest.TestCase):
    def testSimple(self):
        class C(gobject.GObject):
            str = gobject.property(type=str)
            int = gobject.property(type=int)
            float = gobject.property(type=float)
            long = gobject.property(type=long)

        self.failUnless(hasattr(C.props, 'str'))
        self.failUnless(hasattr(C.props, 'int'))
        self.failUnless(hasattr(C.props, 'float'))
        self.failUnless(hasattr(C.props, 'long'))

        o = C()
        self.assertEqual(o.str, '')
        o.str = 'str'
        self.assertEqual(o.str, 'str')

        self.assertEqual(o.int, 0)
        o.int = 1138
        self.assertEqual(o.int, 1138)

        self.assertEqual(o.float, 0.0)
        o.float = 3.14
        self.assertEqual(o.float, 3.14)

        self.assertEqual(o.long, 0L)
        o.long = 100L
        self.assertEqual(o.long, 100L)

    def testCustomGetter(self):
        class C(gobject.GObject):
            def get_prop(self):
                return 'value'
            prop = gobject.property(getter=get_prop)

        o = C()
        self.assertEqual(o.prop, 'value')
        self.assertRaises(TypeError, setattr, o, 'prop', 'xxx')

    def testCustomSetter(self):
        class C(gobject.GObject):
            def set_prop(self, value):
                self._value = value
            prop = gobject.property(setter=set_prop)

            def __init__(self):
                self._value = None
                gobject.GObject.__init__(self)

        o = C()
        self.assertEquals(o._value, None)
        o.prop = 'bar'
        self.assertEquals(o._value, 'bar')
        self.assertRaises(TypeError, getattr, o, 'prop')

    def testErrors(self):
        self.assertRaises(TypeError, gobject.property, type='str')
        self.assertRaises(TypeError, gobject.property, nick=False)
        self.assertRaises(TypeError, gobject.property, blurb=False)
        # this never fail while bool is a subclass of int
        # >>> bool.__bases__
        # (<type 'int'>,)
        # self.assertRaises(TypeError, gobject.property, type=bool, default=0)
        self.assertRaises(TypeError, gobject.property, type=bool, default='ciao mamma')
        self.assertRaises(TypeError, gobject.property, type=bool)
        self.assertRaises(TypeError, gobject.property, type=GEnum)
        self.assertRaises(TypeError, gobject.property, type=GEnum, default=0)
        self.assertRaises(TypeError, gobject.property, type=object, default=0)
        self.assertRaises(TypeError, gobject.property, type=complex)
        self.assertRaises(TypeError, gobject.property, flags=-10)

    def testDefaults(self):
        p1 = gobject.property(type=bool, default=True)
        p2 = gobject.property(type=bool, default=False)

    def testNameWithUnderscore(self):
        class C(gobject.GObject):
            prop_name = gobject.property(type=int)
        o = C()
        o.prop_name = 10
        self.assertEqual(o.prop_name, 10)

    def testRange(self):
        maxint64 = 2 ** 62 - 1
        minint64 = -2 ** 62 - 1
        maxuint64 = 2 ** 63 - 1

        types = [
            (TYPE_INT, G_MININT, G_MAXINT),
            (TYPE_UINT, 0, G_MAXUINT),
            (TYPE_LONG, G_MINLONG, G_MAXLONG),
            (TYPE_ULONG, 0, G_MAXULONG),
            (TYPE_INT64, minint64, maxint64),
            (TYPE_UINT64, 0, maxuint64),
            ]

        for gtype, min, max in types:
            # Normal, everything is alright
            prop = gobject.property(type=gtype, minimum=min, maximum=max)
            subtype = type('', (gobject.GObject,),
                         dict(prop=prop))
            self.assertEqual(subtype.props.prop.minimum, min)
            self.assertEqual(subtype.props.prop.maximum, max)

            # Lower than minimum
            self.assertRaises(TypeError,
                              gobject.property, type=gtype, minimum=min-1,
                              maximum=max)

            # Higher than maximum
            self.assertRaises(TypeError,
                              gobject.property, type=gtype, minimum=min,
                              maximum=max+1)

    def testMultipleInstances(self):
        class C(gobject.GObject):
            prop = gobject.property(type=str, default='default')

        o1 = C()
        o2 = C()
        self.assertEqual(o1.prop, 'default')
        self.assertEqual(o2.prop, 'default')
        o1.prop = 'value'
        self.assertEqual(o1.prop, 'value')
        self.assertEqual(o2.prop, 'default')

    def testObjectProperty(self):
        class PropertyObject(GObject):
            obj = gobject.property(type=GObject)

        pobj1 = PropertyObject()
        obj1_hash = hash(pobj1)
        pobj2 = PropertyObject()

        pobj2.obj = pobj1
        del pobj1
        pobj1 = pobj2.obj
        self.assertEqual(hash(pobj1), obj1_hash)

    def testPropertySubclass(self):
        # test for #470718
        class A(GObject):
            prop1 = gobject.property(type=int)

        class B(A):
            prop2 = gobject.property(type=int)

        b = B()
        b.prop2 = 10
        self.assertEquals(b.prop2, 10)
        b.prop1 = 20
        self.assertEquals(b.prop1, 20)


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


syntax highlighted by Code2HTML, v. 0.9.1